1 /* Python plug-in for dia
2  * Copyright (C) 1999  James Henstridge
3  * Copyright (C) 2000  Hans Breuer
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  */
19 
20 #include <config.h>
21 
22 #include "pydia-object.h"
23 #include "pydia-geometry.h"
24 
25 
26 /* Implements wrappers for Point, Rectangle, IntRectangle, BezPoint */
27 
28 /*
29  * New
30  */
PyDiaPoint_New(Point * pt)31 PyObject* PyDiaPoint_New (Point* pt)
32 {
33   PyDiaPoint *self;
34 
35   self = PyObject_NEW(PyDiaPoint, &PyDiaPoint_Type);
36   if (!self) return NULL;
37 
38   self->pt = *pt;
39 
40   return (PyObject *)self;
41 }
42 
43 PyObject*
PyDiaPointTuple_New(Point * pts,int num)44 PyDiaPointTuple_New (Point* pts, int num)
45 {
46   PyObject* ret;
47   int i;
48 
49   ret = PyTuple_New (num);
50   if (ret) {
51     for (i = 0; i < num; i++)
52       PyTuple_SetItem(ret, i, PyDiaPoint_New(&(pts[i])));
53   }
54 
55   return ret;
56 }
57 
58 /* one of the parameters needs to be NULL, the other is created */
59 PyObject*
PyDiaRectangle_New(Rectangle * r,IntRectangle * ri)60 PyDiaRectangle_New (Rectangle* r, IntRectangle* ri)
61 {
62   PyDiaRectangle *self;
63 
64   self = PyObject_NEW(PyDiaRectangle, &PyDiaRectangle_Type);
65   if (!self) return NULL;
66 
67   self->is_int = (ri != NULL);
68   if (self->is_int)
69     self->r.ri = *ri;
70   else
71     self->r.rf = *r;
72 
73   return (PyObject *)self;
74 }
75 
PyDiaRectangle_New_FromPoints(Point * ul,Point * lr)76 PyObject* PyDiaRectangle_New_FromPoints (Point* ul, Point* lr)
77 {
78   PyDiaRectangle *self;
79 
80   self = PyObject_NEW(PyDiaRectangle, &PyDiaRectangle_Type);
81   if (!self) return NULL;
82 
83   self->is_int = FALSE;
84   self->r.rf.left = ul->x;
85   self->r.rf.top = ul->y;
86   self->r.rf.right = lr->x;
87   self->r.rf.bottom = lr->y;
88 
89   return (PyObject *)self;
90 }
91 
92 
PyDiaBezPoint_New(BezPoint * bpn)93 PyObject* PyDiaBezPoint_New (BezPoint* bpn)
94 {
95   PyDiaBezPoint *self;
96 
97   self = PyObject_NEW(PyDiaBezPoint, &PyDiaBezPoint_Type);
98   if (!self) return NULL;
99 
100   self->bpn = *bpn;
101 
102   return (PyObject *)self;
103 }
104 
105 PyObject*
PyDiaBezPointTuple_New(BezPoint * pts,int num)106 PyDiaBezPointTuple_New (BezPoint* pts, int num)
107 {
108   PyObject* ret;
109   int i;
110 
111   ret = PyTuple_New (num);
112   if (ret) {
113     for (i = 0; i < num; i++)
114       PyTuple_SetItem(ret, i, PyDiaBezPoint_New(&(pts[i])));
115   }
116 
117   return ret;
118 }
119 
PyDiaArrow_New(Arrow * arrow)120 PyObject* PyDiaArrow_New (Arrow* arrow)
121 {
122   PyDiaArrow *self;
123 
124   self = PyObject_NEW(PyDiaArrow, &PyDiaArrow_Type);
125   if (!self) return NULL;
126 
127   self->arrow = *arrow;
128 
129   return (PyObject *)self;
130 }
131 
132 /*
133  * Dealloc
134  */
135 static void
PyDiaGeometry_Dealloc(void * self)136 PyDiaGeometry_Dealloc(void *self)
137 {
138      PyObject_DEL(self);
139 }
140 
141 /*
142  * Compare ?
143  */
144 static int
PyDiaPoint_Compare(PyDiaPoint * self,PyDiaPoint * other)145 PyDiaPoint_Compare(PyDiaPoint *self,
146 			     PyDiaPoint *other)
147 {
148 #if 1
149   return memcmp (&self->pt, &other->pt, sizeof(Point));
150 #else /* ? */
151   if (self->pt.x == other->pt.x && self->pt.x == other->pt.x) return 0;
152 #define SQR(pt) (pt.x*pt.y)
153   if (SQR(self->pt) > SQR(other->pt)) return -1;
154 #undef  SQR
155   return 1;
156 #endif
157 }
158 
159 static int
PyDiaRectangle_Compare(PyDiaRectangle * self,PyDiaRectangle * other)160 PyDiaRectangle_Compare(PyDiaRectangle *self,
161 			     PyDiaRectangle *other)
162 {
163   /* this is not correct */
164   return memcmp (&self->r, &other->r, sizeof(Rectangle));
165 }
166 
167 static int
PyDiaBezPoint_Compare(PyDiaBezPoint * self,PyDiaBezPoint * other)168 PyDiaBezPoint_Compare(PyDiaBezPoint *self,
169 			     PyDiaBezPoint *other)
170 {
171   return memcmp (&self->bpn, &other->bpn, sizeof(BezPoint));
172 }
173 
174 static int
PyDiaArrow_Compare(PyDiaArrow * self,PyDiaArrow * other)175 PyDiaArrow_Compare(PyDiaArrow *self,
176 			 PyDiaArrow *other)
177 {
178   return memcmp (&self->arrow, &other->arrow, sizeof(Arrow));
179 }
180 
181 /*
182  * Hash
183  */
184 static long
PyDiaGeometry_Hash(PyObject * self)185 PyDiaGeometry_Hash(PyObject *self)
186 {
187     return (long)self;
188 }
189 
190 /*
191  * GetAttr
192  */
193 static PyObject *
PyDiaPoint_GetAttr(PyDiaPoint * self,gchar * attr)194 PyDiaPoint_GetAttr(PyDiaPoint *self, gchar *attr)
195 {
196   if (!strcmp(attr, "__members__"))
197     return Py_BuildValue("[ss]", "x", "y");
198   else if (!strcmp(attr, "x"))
199     return PyFloat_FromDouble(self->pt.x);
200   else if (!strcmp(attr, "y"))
201     return PyFloat_FromDouble(self->pt.y);
202 
203   PyErr_SetString(PyExc_AttributeError, attr);
204   return NULL;
205 }
206 
207 static PyObject *
PyDiaRectangle_GetAttr(PyDiaRectangle * self,gchar * attr)208 PyDiaRectangle_GetAttr(PyDiaRectangle *self, gchar *attr)
209 {
210 #define I_OR_F(v) \
211   (self->is_int ? \
212    PyInt_FromLong(self->r.ri. v) : PyFloat_FromDouble(self->r.rf. v))
213 
214   if (!strcmp(attr, "__members__"))
215     return Py_BuildValue("[ssss]", "top", "left", "right", "bottom" );
216   else if (!strcmp(attr, "top"))
217     return I_OR_F(top);
218   else if (!strcmp(attr, "left"))
219     return I_OR_F(left);
220   else if (!strcmp(attr, "right"))
221     return I_OR_F(right);
222   else if (!strcmp(attr, "bottom"))
223     return I_OR_F(bottom);
224 
225   PyErr_SetString(PyExc_AttributeError, attr);
226   return NULL;
227 
228 #undef I_O_F
229 }
230 
231 static PyObject *
PyDiaBezPoint_GetAttr(PyDiaBezPoint * self,gchar * attr)232 PyDiaBezPoint_GetAttr(PyDiaBezPoint *self, gchar *attr)
233 {
234   if (!strcmp(attr, "__members__"))
235     return Py_BuildValue("[ssss]", "type", "p1", "p2", "p3");
236   else if (!strcmp(attr, "type"))
237     return PyInt_FromLong(self->bpn.type);
238   else if (!strcmp(attr, "p1"))
239     return PyDiaPoint_New(&(self->bpn.p1));
240   else if (!strcmp(attr, "p2"))
241     return PyDiaPoint_New(&(self->bpn.p2));
242   else if (!strcmp(attr, "p3"))
243     return PyDiaPoint_New(&(self->bpn.p3));
244 
245   PyErr_SetString(PyExc_AttributeError, attr);
246   return NULL;
247 }
248 
249 static PyObject *
PyDiaArrow_GetAttr(PyDiaArrow * self,gchar * attr)250 PyDiaArrow_GetAttr(PyDiaArrow *self, gchar *attr)
251 {
252   if (!strcmp(attr, "__members__"))
253     return Py_BuildValue("[sss]", "type", "width", "length");
254   else if (!strcmp(attr, "type"))
255     return PyInt_FromLong(self->arrow.type);
256   else if (!strcmp(attr, "width"))
257     return PyFloat_FromDouble(self->arrow.width);
258   else if (!strcmp(attr, "length"))
259     return PyFloat_FromDouble(self->arrow.length);
260 
261   PyErr_SetString(PyExc_AttributeError, attr);
262   return NULL;
263 }
264 
265 /*
266  * SetAttr
267  */
268 
269 /* XXX */
270 
271 /*
272  * Repr / _Str
273  */
274 static PyObject *
PyDiaPoint_Str(PyDiaPoint * self)275 PyDiaPoint_Str(PyDiaPoint *self)
276 {
277     PyObject* py_s;
278 #ifndef _DEBUG /* gives crashes with nan */
279     gchar* s = g_strdup_printf ("(%f,%f)",
280                                 (float)(self->pt.x), (float)(self->pt.y));
281 #else
282     gchar* s = g_strdup_printf ("(%e,%e)",
283                                 (float)(self->pt.x), (float)(self->pt.y));
284 #endif
285     py_s = PyString_FromString(s);
286     g_free(s);
287     return py_s;
288 }
289 
290 static PyObject *
PyDiaRectangle_Str(PyDiaRectangle * self)291 PyDiaRectangle_Str(PyDiaRectangle *self)
292 {
293     PyObject* py_s;
294     gchar* s;
295     if (self->is_int)
296       s = g_strdup_printf ("((%d,%d),(%d,%d))",
297                            (self->r.ri.left), (self->r.ri.top),
298                            (self->r.ri.right), (self->r.ri.bottom));
299     else
300 #ifndef _DEBUG /* gives crashes with nan */
301       s = g_strdup_printf ("((%f,%f),(%f,%f))",
302                            (float)(self->r.rf.left), (float)(self->r.rf.top),
303                            (float)(self->r.rf.right), (float)(self->r.rf.bottom));
304 #else
305       s = g_strdup_printf ("((%e,%e),(%e,%e))",
306                            (float)(self->r.rf.left), (float)(self->r.rf.top),
307                            (float)(self->r.rf.right), (float)(self->r.rf.bottom));
308 #endif
309     py_s = PyString_FromString(s);
310     g_free(s);
311     return py_s;
312 }
313 
314 static PyObject *
PyDiaBezPoint_Str(PyDiaBezPoint * self)315 PyDiaBezPoint_Str(PyDiaBezPoint *self)
316 {
317     PyObject* py_s;
318 #if 0 /* FIXME: this is causing bad crashes with unintialized points.
319        * Probably a glib and a Dia problem ... */
320     gchar* s = g_strdup_printf ("((%f,%f),(%f,%f),(%f,%f),%s)",
321                                 (float)(self->bpn.p1.x), (float)(self->bpn.p1.y),
322                                 (float)(self->bpn.p2.x), (float)(self->bpn.p2.y),
323                                 (float)(self->bpn.p3.x), (float)(self->bpn.p3.y),
324                                 (self->bpn.type == BEZ_MOVE_TO ? "MOVE_TO" :
325                                   (self->bpn.type == BEZ_LINE_TO ? "LINE_TO" : "CURVE_TO")));
326 #else
327     gchar* s = g_strdup_printf ("%s",
328                                 (self->bpn.type == BEZ_MOVE_TO ? "MOVE_TO" :
329                                   (self->bpn.type == BEZ_LINE_TO ? "LINE_TO" : "CURVE_TO")));
330 #endif
331     py_s = PyString_FromString(s);
332     g_free(s);
333     return py_s;
334 }
335 
336 
337 static PyObject *
PyDiaArrow_Str(PyDiaArrow * self)338 PyDiaArrow_Str(PyDiaArrow *self)
339 {
340     PyObject* py_s;
341     gchar* s = g_strdup_printf ("(%f,%f, %d)",
342                                 (float)(self->arrow.width),
343                                 (float)(self->arrow.length),
344                                 (int)(self->arrow.type));
345     py_s = PyString_FromString(s);
346     g_free(s);
347     return py_s;
348 }
349 
350 /*
351  * sequence interface (query only)
352  */
353 /* Point */
354 static int
point_length(PyDiaRectangle * self)355 point_length(PyDiaRectangle *self)
356 {
357   return 2;
358 }
359 static PyObject *
point_item(PyDiaPoint * self,int i)360 point_item(PyDiaPoint* self, int i)
361 {
362   switch (i) {
363   case 0 : return PyDiaPoint_GetAttr(self, "x");
364   case 1 : return PyDiaPoint_GetAttr(self, "y");
365   default :
366     PyErr_SetString(PyExc_IndexError, "PyDiaPoint index out of range");
367     return NULL;
368   }
369 }
370 static PyObject *
point_slice(PyDiaPoint * self,int i,int j)371 point_slice(PyDiaPoint* self, int i, int j)
372 {
373   PyObject *ret;
374 
375   /* j maybe negative */
376   if (j <= 0)
377     j = 1 + j;
378   /* j may be rather huge [:] ^= 0:0x7FFFFFFF */
379   if (j > 1)
380     j = 1;
381   ret = PyTuple_New(j - i + 1);
382   if (ret) {
383     int k;
384     for (k = i; k <= j && k < 2; k++)
385       PyTuple_SetItem(ret, k - i, point_item(self, k));
386   }
387   return ret;
388 }
389 
390 static PySequenceMethods point_as_sequence = {
391 	(inquiry)point_length, /*sq_length*/
392 	(binaryfunc)0, /*sq_concat*/
393 	(intargfunc)0, /*sq_repeat*/
394 	(intargfunc)point_item, /*sq_item*/
395 	(intintargfunc)point_slice, /*sq_slice*/
396 	0,		/*sq_ass_item*/
397 	0,		/*sq_ass_slice*/
398 	(objobjproc)0 /*sq_contains*/
399 };
400 
401 /* Rect */
402 static int
rect_length(PyDiaRectangle * self)403 rect_length(PyDiaRectangle *self)
404 {
405   return 4;
406 }
407 static PyObject *
rect_item(PyDiaRectangle * self,int i)408 rect_item(PyDiaRectangle* self, int i)
409 {
410   switch (i) {
411   case 0 : return PyDiaRectangle_GetAttr(self, "left");
412   case 1 : return PyDiaRectangle_GetAttr(self, "top");
413   case 2 : return PyDiaRectangle_GetAttr(self, "right");
414   case 3 : return PyDiaRectangle_GetAttr(self, "bottom");
415   default :
416     PyErr_SetString(PyExc_IndexError, "PyDiaRectangle index out of range");
417     return NULL;
418   }
419 }
420 static PyObject *
rect_slice(PyDiaRectangle * self,int i,int j)421 rect_slice(PyDiaRectangle* self, int i, int j)
422 {
423   PyObject *ret;
424 
425   /* j maybe negative */
426   if (j <= 0)
427     j = 3 + j;
428   /* j may be rather huge [:] ^= 0:0x7FFFFFFF */
429   if (j > 3)
430     j = 3;
431   ret = PyTuple_New(j - i + 1);
432   if (ret) {
433     int k;
434     for (k = i; k <= j && k < 4; k++)
435       PyTuple_SetItem(ret, k - i, rect_item(self, k));
436   }
437   return ret;
438 }
439 
440 static PySequenceMethods rect_as_sequence = {
441 	(inquiry)rect_length, /*sq_length*/
442 	(binaryfunc)0, /*sq_concat*/
443 	(intargfunc)0, /*sq_repeat*/
444 	(intargfunc)rect_item, /*sq_item*/
445 	(intintargfunc)rect_slice, /*sq_slice*/
446 	0,		/*sq_ass_item*/
447 	0,		/*sq_ass_slice*/
448 	(objobjproc)0 /*sq_contains*/
449 };
450 
451 /*
452  * Python objetcs
453  */
454 PyTypeObject PyDiaPoint_Type = {
455     PyObject_HEAD_INIT(&PyType_Type)
456     0,
457     "dia.Point",
458     sizeof(PyDiaPoint),
459     0,
460     (destructor)PyDiaGeometry_Dealloc,
461     (printfunc)0,
462     (getattrfunc)PyDiaPoint_GetAttr,
463     (setattrfunc)0,
464     (cmpfunc)PyDiaPoint_Compare,
465     (reprfunc)0,
466     0, /* as_number */
467     &point_as_sequence,
468     0,
469     (hashfunc)PyDiaGeometry_Hash,
470     (ternaryfunc)0,
471     (reprfunc)PyDiaPoint_Str,
472     (getattrofunc)0,
473     (setattrofunc)0,
474     (PyBufferProcs *)0,
475     0L, /* Flags */
476     "The dia.Point does not only provide access trough it's members but also via a sequence interface."
477 };
478 
479 PyTypeObject PyDiaRectangle_Type = {
480     PyObject_HEAD_INIT(&PyType_Type)
481     0,
482     "dia.Rectangle",
483     sizeof(PyDiaRectangle),
484     0,
485     (destructor)PyDiaGeometry_Dealloc,
486     (printfunc)0,
487     (getattrfunc)PyDiaRectangle_GetAttr,
488     (setattrfunc)0,
489     (cmpfunc)PyDiaRectangle_Compare,
490     (reprfunc)0,
491     0, /* as_number */
492     &rect_as_sequence,
493     0, /* as_mapping */
494     (hashfunc)PyDiaGeometry_Hash,
495     (ternaryfunc)0,
496     (reprfunc)PyDiaRectangle_Str,
497     (getattrofunc)0,
498     (setattrofunc)0,
499     (PyBufferProcs *)0,
500     0L, /* Flags */
501     "The dia.Rectangle does not only provide access trough it's members but also via a sequence interface."
502 };
503 
504 PyTypeObject PyDiaBezPoint_Type = {
505     PyObject_HEAD_INIT(&PyType_Type)
506     0,
507     "dia.BezPoint",
508     sizeof(PyDiaBezPoint),
509     0,
510     (destructor)PyDiaGeometry_Dealloc,
511     (printfunc)0,
512     (getattrfunc)PyDiaBezPoint_GetAttr,
513     (setattrfunc)0,
514     (cmpfunc)PyDiaBezPoint_Compare,
515     (reprfunc)0,
516     0,
517     0,
518     0,
519     (hashfunc)PyDiaGeometry_Hash,
520     (ternaryfunc)0,
521     (reprfunc)PyDiaBezPoint_Str,
522     (getattrofunc)0,
523     (setattrofunc)0,
524     (PyBufferProcs *)0,
525     0L, /* Flags */
526     "A dia.Point, a bezier type and two control points (dia.Point) make a bezier point."
527 };
528 
529 PyTypeObject PyDiaArrow_Type = {
530     PyObject_HEAD_INIT(&PyType_Type)
531     0,
532     "dia.Arrow",
533     sizeof(PyDiaArrow),
534     0,
535     (destructor)PyDiaGeometry_Dealloc,
536     (printfunc)0,
537     (getattrfunc)PyDiaArrow_GetAttr,
538     (setattrfunc)0,
539     (cmpfunc)PyDiaArrow_Compare,
540     (reprfunc)0,
541     0,
542     0,
543     0,
544     (hashfunc)PyDiaGeometry_Hash,
545     (ternaryfunc)0,
546     (reprfunc)PyDiaArrow_Str,
547     (getattrofunc)0,
548     (setattrofunc)0,
549     (PyBufferProcs *)0,
550     0L, /* Flags */
551     "Dia's line objects usually ends with an dia.Arrow"
552 };
553