1 /* -*- Mode: C; c-basic-offset: 4 -*-
2  * Gimp-Python - allows the writing of Gimp plugins in Python.
3  * Copyright (C) 2006  Manish Singh <yosh@gimp.org>
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 3 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, see <https://www.gnu.org/licenses/>.
17  */
18 
19 #ifdef HAVE_CONFIG_H
20 #  include <config.h>
21 #endif
22 
23 #include "pygimp.h"
24 
25 
26 static PyObject *vectors_bezier_stroke_new(PyGimpVectors *vectors, int stroke);
27 
28 
29 typedef struct {
30     PyObject_HEAD
31     gint32 vectors_ID;
32     int stroke;
33 } PyGimpVectorsStroke;
34 
35 static PyObject *
vs_get_length(PyGimpVectorsStroke * self,PyObject * args,PyObject * kwargs)36 vs_get_length(PyGimpVectorsStroke *self, PyObject *args, PyObject *kwargs)
37 {
38     double precision;
39     double length;
40 
41     static char *kwlist[] = { "precision", NULL };
42 
43     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "d:get_length", kwlist,
44                                      &precision))
45         return NULL;
46 
47     length = gimp_vectors_stroke_get_length(self->vectors_ID, self->stroke,
48                                             precision);
49 
50     return PyFloat_FromDouble(length);
51 }
52 
53 static PyObject *
vs_get_point_at_dist(PyGimpVectorsStroke * self,PyObject * args,PyObject * kwargs)54 vs_get_point_at_dist(PyGimpVectorsStroke *self, PyObject *args, PyObject *kwargs)
55 {
56     double dist, precision;
57     double x, y, slope;
58     gboolean valid;
59     PyObject *ret;
60 
61     static char *kwlist[] = { "dist", "precision", NULL };
62 
63     if (!PyArg_ParseTupleAndKeywords(args, kwargs,
64                                      "dd:get_point_at_dist", kwlist,
65                                      &dist, &precision))
66         return NULL;
67 
68     gimp_vectors_stroke_get_point_at_dist(self->vectors_ID, self->stroke,
69                                           dist, precision,
70                                           &x, &y, &slope, &valid);
71 
72     ret = PyTuple_New(4);
73     if (ret == NULL)
74         return NULL;
75 
76     PyTuple_SetItem(ret, 0, PyFloat_FromDouble(x));
77     PyTuple_SetItem(ret, 1, PyFloat_FromDouble(y));
78     PyTuple_SetItem(ret, 2, PyFloat_FromDouble(slope));
79     PyTuple_SetItem(ret, 3, PyBool_FromLong(valid));
80 
81     return ret;
82 }
83 
84 static PyObject *
vs_close(PyGimpVectorsStroke * self)85 vs_close(PyGimpVectorsStroke *self)
86 {
87     gimp_vectors_stroke_close(self->vectors_ID, self->stroke);
88     Py_INCREF(Py_None);
89     return Py_None;
90 }
91 
92 
93 static PyObject *
vs_translate(PyGimpVectorsStroke * self,PyObject * args,PyObject * kwargs)94 vs_translate(PyGimpVectorsStroke *self, PyObject *args, PyObject *kwargs)
95 {
96     double off_x, off_y;
97 
98     static char *kwlist[] = { "off_x", "off_y", NULL };
99 
100     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "dd:translate", kwlist,
101                                      &off_x, &off_y))
102         return NULL;
103 
104     gimp_vectors_stroke_translate(self->vectors_ID, self->stroke, off_x, off_y);
105 
106     Py_INCREF(Py_None);
107     return Py_None;
108 }
109 
110 static PyObject *
vs_scale(PyGimpVectorsStroke * self,PyObject * args,PyObject * kwargs)111 vs_scale(PyGimpVectorsStroke *self, PyObject *args, PyObject *kwargs)
112 {
113     double scale_x, scale_y;
114 
115     static char *kwlist[] = { "scale_x", "scale_y", NULL };
116 
117     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "dd:scale", kwlist,
118                                      &scale_x, &scale_y))
119         return NULL;
120 
121     gimp_vectors_stroke_scale(self->vectors_ID, self->stroke, scale_x, scale_y);
122 
123     Py_INCREF(Py_None);
124     return Py_None;
125 }
126 
127 static PyObject *
vs_rotate(PyGimpVectorsStroke * self,PyObject * args,PyObject * kwargs)128 vs_rotate(PyGimpVectorsStroke *self, PyObject *args, PyObject *kwargs)
129 {
130     double center_x, center_y, angle;
131 
132     static char *kwlist[] = { "center_x", "center_y", "angle", NULL };
133 
134     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ddd:rotate", kwlist,
135                                      &center_x, &center_y, &angle))
136         return NULL;
137 
138     gimp_vectors_stroke_rotate(self->vectors_ID, self->stroke, center_x,
139                                center_y, angle);
140 
141     Py_INCREF(Py_None);
142     return Py_None;
143 }
144 
145 static PyObject *
vs_flip(PyGimpVectorsStroke * self,PyObject * args,PyObject * kwargs)146 vs_flip(PyGimpVectorsStroke *self, PyObject *args, PyObject *kwargs)
147 {
148     int    flip_type;
149     double axis;
150 
151     static char *kwlist[] = { "flip_type", "axis", NULL };
152 
153     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "id:flip", kwlist,
154                                      &flip_type, &axis))
155         return NULL;
156 
157     gimp_vectors_stroke_flip(self->vectors_ID, self->stroke, flip_type, axis);
158 
159     Py_INCREF(Py_None);
160     return Py_None;
161 }
162 
163 static PyObject *
vs_flip_free(PyGimpVectorsStroke * self,PyObject * args,PyObject * kwargs)164 vs_flip_free(PyGimpVectorsStroke *self, PyObject *args, PyObject *kwargs)
165 {
166     double x1,y1,x2,y2;
167 
168     static char *kwlist[] = { "x1", "y1", "x2", "y2", NULL };
169 
170     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "dddd:flip_free", kwlist,
171                                      &x1, &y1, &x2, &y2))
172         return NULL;
173 
174     gimp_vectors_stroke_flip_free(self->vectors_ID, self->stroke,
175                                   x1, y1, x2, y2);
176     Py_INCREF(Py_None);
177     return Py_None;
178 }
179 
180 
181 
182 static PyObject *
vs_interpolate(PyGimpVectorsStroke * self,PyObject * args,PyObject * kwargs)183 vs_interpolate(PyGimpVectorsStroke *self, PyObject *args, PyObject *kwargs)
184 {
185     double precision;
186     double *coords;
187     int i, num_coords;
188     gboolean closed;
189     PyObject *ret, *ret_coords;
190 
191     static char *kwlist[] = { "precision", NULL };
192 
193     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "d:interpolate", kwlist,
194                                      &precision))
195         return NULL;
196 
197     coords = gimp_vectors_stroke_interpolate(self->vectors_ID, self->stroke,
198                                              precision, &num_coords, &closed);
199 
200     ret = PyTuple_New(2);
201     if (ret == NULL)
202         return NULL;
203 
204     ret_coords = PyList_New(num_coords);
205     if (ret_coords == NULL) {
206         Py_DECREF(ret);
207         return NULL;
208     }
209 
210     for (i = 0; i < num_coords; i++)
211         PyList_SetItem(ret_coords, i, PyFloat_FromDouble(coords[i]));
212 
213     PyTuple_SetItem(ret, 0, ret_coords);
214     PyTuple_SetItem(ret, 1, PyBool_FromLong(closed));
215 
216     return ret;
217 }
218 
219 static PyMethodDef vs_methods[] = {
220     { "get_length", (PyCFunction)vs_get_length, METH_VARARGS | METH_KEYWORDS },
221     { "get_point_at_dist", (PyCFunction)vs_get_point_at_dist, METH_VARARGS | METH_KEYWORDS },
222     { "close", (PyCFunction)vs_close, METH_NOARGS },
223     { "translate", (PyCFunction)vs_translate, METH_VARARGS | METH_KEYWORDS },
224     { "scale", (PyCFunction)vs_scale, METH_VARARGS | METH_KEYWORDS },
225     { "rotate", (PyCFunction)vs_rotate, METH_VARARGS | METH_KEYWORDS },
226     { "flip", (PyCFunction)vs_flip, METH_VARARGS | METH_KEYWORDS },
227     { "flip_free", (PyCFunction)vs_flip_free, METH_VARARGS | METH_KEYWORDS },
228     { "interpolate", (PyCFunction)vs_interpolate, METH_VARARGS | METH_KEYWORDS },
229     { NULL, NULL, 0 }
230 };
231 
232 static PyObject *
vs_get_ID(PyGimpVectorsStroke * self,void * closure)233 vs_get_ID(PyGimpVectorsStroke *self, void *closure)
234 {
235     return PyInt_FromLong(self->stroke);
236 }
237 
238 static PyObject *
vs_get_vectors_ID(PyGimpVectorsStroke * self,void * closure)239 vs_get_vectors_ID(PyGimpVectorsStroke *self, void *closure)
240 {
241     return PyInt_FromLong(self->vectors_ID);
242 }
243 
244 static PyObject *
vs_get_points(PyGimpVectorsStroke * self,void * closure)245 vs_get_points(PyGimpVectorsStroke *self, void *closure)
246 {
247     double *controlpoints;
248     int i, num_points;
249     gboolean closed;
250     PyObject *ret, *ret_points;
251 
252     gimp_vectors_stroke_get_points(self->vectors_ID, self->stroke,
253                                    &num_points, &controlpoints, &closed);
254 
255     ret = PyTuple_New(2);
256     if (ret == NULL)
257         return NULL;
258 
259     ret_points = PyList_New(num_points);
260     if (ret_points == NULL) {
261         Py_DECREF(ret);
262         return NULL;
263     }
264 
265     for (i = 0; i < num_points; i++)
266         PyList_SetItem(ret_points, i, PyFloat_FromDouble(controlpoints[i]));
267 
268     PyTuple_SetItem(ret, 0, ret_points);
269     PyTuple_SetItem(ret, 1, PyBool_FromLong(closed));
270 
271     return ret;
272 }
273 
274 static PyGetSetDef vs_getsets[] = {
275     { "ID", (getter)vs_get_ID, (setter)0 },
276     { "vectors_ID", (getter)vs_get_vectors_ID, (setter)0 },
277     { "points", (getter)vs_get_points, (setter)0 },
278     { NULL, (getter)0, (setter)0 }
279 };
280 
281 static void
vs_dealloc(PyGimpVectorsStroke * self)282 vs_dealloc(PyGimpVectorsStroke *self)
283 {
284     PyObject_DEL(self);
285 }
286 
287 static PyObject *
vs_repr(PyGimpVectorsStroke * self)288 vs_repr(PyGimpVectorsStroke *self)
289 {
290     PyObject *s;
291     char *name;
292 
293     name = gimp_item_get_name(self->vectors_ID);
294     s = PyString_FromFormat("<gimp.VectorsStroke %d of gimp.Vectors '%s'>",
295                             self->stroke, name ? name : "(null)");
296     g_free(name);
297 
298     return s;
299 }
300 
301 static int
vs_cmp(PyGimpVectorsStroke * self,PyGimpVectorsStroke * other)302 vs_cmp(PyGimpVectorsStroke *self, PyGimpVectorsStroke *other)
303 {
304     if (self->vectors_ID == other->vectors_ID) {
305         if (self->stroke == other->stroke)
306             return 0;
307         if (self->stroke > other->stroke)
308             return -1;
309         return 1;
310     }
311     if (self->vectors_ID > other->vectors_ID)
312         return -1;
313     return 1;
314 }
315 
316 PyTypeObject PyGimpVectorsStroke_Type = {
317     PyObject_HEAD_INIT(NULL)
318     0,                                  /* ob_size */
319     "gimp.VectorsStroke",               /* tp_name */
320     sizeof(PyGimpVectorsStroke),        /* tp_basicsize */
321     0,                                  /* tp_itemsize */
322     /* methods */
323     (destructor)vs_dealloc,             /* tp_dealloc */
324     (printfunc)0,                       /* tp_print */
325     (getattrfunc)0,                     /* tp_getattr */
326     (setattrfunc)0,                     /* tp_setattr */
327     (cmpfunc)vs_cmp,                    /* tp_compare */
328     (reprfunc)vs_repr,                  /* tp_repr */
329     0,                                  /* tp_as_number */
330     0,                                  /* tp_as_sequence */
331     0,                                  /* tp_as_mapping */
332     (hashfunc)0,                        /* tp_hash */
333     (ternaryfunc)0,                     /* tp_call */
334     (reprfunc)0,                        /* tp_str */
335     (getattrofunc)0,                    /* tp_getattro */
336     (setattrofunc)0,                    /* tp_setattro */
337     0,                                  /* tp_as_buffer */
338     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
339     NULL, /* Documentation string */
340     (traverseproc)0,                    /* tp_traverse */
341     (inquiry)0,                         /* tp_clear */
342     (richcmpfunc)0,                     /* tp_richcompare */
343     0,                                  /* tp_weaklistoffset */
344     (getiterfunc)0,                     /* tp_iter */
345     (iternextfunc)0,                    /* tp_iternext */
346     vs_methods,                         /* tp_methods */
347     0,                                  /* tp_members */
348     vs_getsets,                         /* tp_getset */
349     (PyTypeObject *)0,                  /* tp_base */
350     (PyObject *)0,                      /* tp_dict */
351     0,                                  /* tp_descr_get */
352     0,                                  /* tp_descr_set */
353     0,                                  /* tp_dictoffset */
354     (initproc)0,                        /* tp_init */
355     (allocfunc)0,                       /* tp_alloc */
356     (newfunc)0,                         /* tp_new */
357 };
358 
359 
360 static PyObject *
vbs_new_moveto(PyTypeObject * type,PyObject * args,PyObject * kwargs)361 vbs_new_moveto(PyTypeObject *type, PyObject *args, PyObject *kwargs)
362 {
363     PyGimpVectors *vectors;
364     double x0, y0;
365     int stroke;
366 
367     static char *kwlist[] = { "vectors", "x0", "y0", NULL };
368 
369     if (!PyArg_ParseTupleAndKeywords(args, kwargs,
370                                      "O!dd:new_moveto", kwlist,
371                                      &PyGimpVectors_Type, &vectors,
372                                      &x0, &y0))
373         return NULL;
374 
375     stroke = gimp_vectors_bezier_stroke_new_moveto(vectors->ID, x0, y0);
376 
377     return vectors_bezier_stroke_new(vectors, stroke);
378 }
379 
380 static PyObject *
vbs_new_ellipse(PyTypeObject * type,PyObject * args,PyObject * kwargs)381 vbs_new_ellipse(PyTypeObject *type, PyObject *args, PyObject *kwargs)
382 {
383     PyGimpVectors *vectors;
384     double x0, y0, radius_x, radius_y, angle;
385     int stroke;
386 
387     static char *kwlist[] = { "vectors", "x0", "y0", "radius_x", "radius_y",
388                               "angle", NULL };
389 
390     if (!PyArg_ParseTupleAndKeywords(args, kwargs,
391                                      "O!ddddd:new_ellipse", kwlist,
392                                      &PyGimpVectors_Type, &vectors,
393                                      &x0, &y0, &radius_x, &radius_y, &angle))
394         return NULL;
395 
396     stroke = gimp_vectors_bezier_stroke_new_ellipse(vectors->ID, x0, y0,
397                                                     radius_x, radius_y, angle);
398 
399     return vectors_bezier_stroke_new(vectors, stroke);
400 }
401 
402 static PyObject *
vbs_lineto(PyGimpVectorsStroke * self,PyObject * args,PyObject * kwargs)403 vbs_lineto(PyGimpVectorsStroke *self, PyObject *args, PyObject *kwargs)
404 {
405     double x0, y0;
406 
407     static char *kwlist[] = { "x0", "y0", NULL };
408 
409     if (!PyArg_ParseTupleAndKeywords(args, kwargs,
410                                      "dd:lineto", kwlist,
411                                      &x0, &y0))
412         return NULL;
413 
414     gimp_vectors_bezier_stroke_lineto(self->vectors_ID, self->stroke, x0, y0);
415 
416     Py_INCREF(Py_None);
417     return Py_None;
418 }
419 
420 static PyObject *
vbs_conicto(PyGimpVectorsStroke * self,PyObject * args,PyObject * kwargs)421 vbs_conicto(PyGimpVectorsStroke *self, PyObject *args, PyObject *kwargs)
422 {
423     double x0, y0, x1, y1;
424 
425     static char *kwlist[] = { "x0", "y0", "x1", "y1", NULL };
426 
427     if (!PyArg_ParseTupleAndKeywords(args, kwargs,
428                                      "dddd:conicto", kwlist,
429                                      &x0, &y0, &x1, &y1))
430         return NULL;
431 
432     gimp_vectors_bezier_stroke_conicto(self->vectors_ID, self->stroke,
433                                        x0, y0, x1, y1);
434 
435     Py_INCREF(Py_None);
436     return Py_None;
437 }
438 
439 static PyObject *
vbs_cubicto(PyGimpVectorsStroke * self,PyObject * args,PyObject * kwargs)440 vbs_cubicto(PyGimpVectorsStroke *self, PyObject *args, PyObject *kwargs)
441 {
442     double x0, y0, x1, y1, x2, y2;
443 
444     static char *kwlist[] = { "x0", "y0", "x1", "y1", "x2", "y2", NULL };
445 
446     if (!PyArg_ParseTupleAndKeywords(args, kwargs,
447                                      "dddddd:cubicto", kwlist,
448                                      &x0, &y0, &x1, &y1, &x2, &y2))
449         return NULL;
450 
451     gimp_vectors_bezier_stroke_cubicto(self->vectors_ID, self->stroke,
452                                        x0, y0, x1, y1, x2, y2);
453 
454     Py_INCREF(Py_None);
455     return Py_None;
456 }
457 
458 static PyMethodDef vbs_methods[] = {
459     { "new_moveto", (PyCFunction)vbs_new_moveto, METH_VARARGS | METH_KEYWORDS | METH_CLASS },
460     { "new_ellipse", (PyCFunction)vbs_new_ellipse, METH_VARARGS | METH_KEYWORDS | METH_CLASS },
461     { "lineto", (PyCFunction)vbs_lineto, METH_VARARGS | METH_KEYWORDS },
462     { "conicto", (PyCFunction)vbs_conicto, METH_VARARGS | METH_KEYWORDS },
463     { "cubicto", (PyCFunction)vbs_cubicto, METH_VARARGS | METH_KEYWORDS },
464     { NULL, NULL, 0 }
465 };
466 
467 static PyObject *
vbs_repr(PyGimpVectorsStroke * self)468 vbs_repr(PyGimpVectorsStroke *self)
469 {
470     PyObject *s;
471     char *name;
472 
473     name = gimp_item_get_name(self->vectors_ID);
474     s = PyString_FromFormat("<gimp.VectorsBezierStroke %d of gimp.Vectors '%s'>",
475                             self->stroke, name ? name : "(null)");
476     g_free(name);
477 
478     return s;
479 }
480 
481 static int
vbs_init(PyGimpVectorsStroke * self,PyObject * args,PyObject * kwargs)482 vbs_init(PyGimpVectorsStroke *self, PyObject *args, PyObject *kwargs)
483 {
484     PyGimpVectors *vectors;
485     double *controlpoints;
486     gboolean closed = FALSE;
487     PyObject *py_controlpoints, *item;
488     int i, num_points;
489 
490     static char *kwlist[] = { "vectors", "controlpoints", "closed", NULL };
491 
492     if (!PyArg_ParseTupleAndKeywords(args, kwargs,
493                                      "O!O|i:gimp.VectorsBezierStroke.__init__",
494                                      kwlist,
495                                      &PyGimpVectors_Type, &vectors,
496                                      &py_controlpoints, &closed))
497         return -1;
498 
499     if (!PySequence_Check(py_controlpoints)) {
500         PyErr_SetString(PyExc_TypeError,
501                         "controlpoints must be a sequence");
502         return -1;
503     }
504 
505     num_points = PySequence_Length(py_controlpoints);
506     controlpoints = g_new(gdouble, num_points);
507 
508     for (i = 0; i < num_points; i++) {
509         item = PySequence_GetItem(py_controlpoints, i);
510 
511         if (!PyFloat_Check(item)) {
512             PyErr_SetString(PyExc_TypeError,
513                             "controlpoints must be a sequence of floats");
514             g_free(controlpoints);
515             return -1;
516         }
517 
518         controlpoints[i] = PyFloat_AsDouble(item);
519     }
520 
521     self->vectors_ID = vectors->ID;
522     self->stroke =
523         gimp_vectors_stroke_new_from_points(self->vectors_ID,
524                                             GIMP_VECTORS_STROKE_TYPE_BEZIER,
525                                             num_points, controlpoints, closed);
526 
527     g_free(controlpoints);
528 
529     return 0;
530 }
531 
532 PyTypeObject PyGimpVectorsBezierStroke_Type = {
533     PyObject_HEAD_INIT(NULL)
534     0,                                  /* ob_size */
535     "gimp.VectorsBezierStroke",         /* tp_name */
536     sizeof(PyGimpVectorsStroke),        /* tp_basicsize */
537     0,                                  /* tp_itemsize */
538     /* methods */
539     (destructor)vs_dealloc,             /* tp_dealloc */
540     (printfunc)0,                       /* tp_print */
541     (getattrfunc)0,                     /* tp_getattr */
542     (setattrfunc)0,                     /* tp_setattr */
543     (cmpfunc)vs_cmp,                    /* tp_compare */
544     (reprfunc)vbs_repr,                 /* tp_repr */
545     0,                                  /* tp_as_number */
546     0,                                  /* tp_as_sequence */
547     0,                                  /* tp_as_mapping */
548     (hashfunc)0,                        /* tp_hash */
549     (ternaryfunc)0,                     /* tp_call */
550     (reprfunc)0,                        /* tp_str */
551     (getattrofunc)0,                    /* tp_getattro */
552     (setattrofunc)0,                    /* tp_setattro */
553     0,                                  /* tp_as_buffer */
554     Py_TPFLAGS_DEFAULT,                 /* tp_flags */
555     NULL, /* Documentation string */
556     (traverseproc)0,                    /* tp_traverse */
557     (inquiry)0,                         /* tp_clear */
558     (richcmpfunc)0,                     /* tp_richcompare */
559     0,                                  /* tp_weaklistoffset */
560     (getiterfunc)0,                     /* tp_iter */
561     (iternextfunc)0,                    /* tp_iternext */
562     vbs_methods,                        /* tp_methods */
563     0,                                  /* tp_members */
564     0,                                  /* tp_getset */
565     &PyGimpVectorsStroke_Type,          /* tp_base */
566     (PyObject *)0,                      /* tp_dict */
567     0,                                  /* tp_descr_get */
568     0,                                  /* tp_descr_set */
569     0,                                  /* tp_dictoffset */
570     (initproc)vbs_init,                 /* tp_init */
571     (allocfunc)0,                       /* tp_alloc */
572     (newfunc)0,                         /* tp_new */
573 };
574 
575 static PyObject *
vectors_bezier_stroke_new(PyGimpVectors * vectors,int stroke)576 vectors_bezier_stroke_new(PyGimpVectors *vectors, int stroke)
577 {
578     PyGimpVectorsStroke *self;
579 
580     self = PyObject_NEW(PyGimpVectorsStroke, &PyGimpVectorsBezierStroke_Type);
581 
582     if (self == NULL)
583         return NULL;
584 
585     self->vectors_ID = vectors->ID;
586     self->stroke = stroke;
587 
588     return (PyObject *)self;
589 }
590 
591 
592 static PyObject *
vectors_remove_stroke(PyGimpVectors * self,PyObject * args,PyObject * kwargs)593 vectors_remove_stroke(PyGimpVectors *self, PyObject *args, PyObject *kwargs)
594 {
595     int stroke_id ;
596     /* PyGimpVectorsStroke *stroke; */
597     PyObject *stroke = NULL;
598 
599     static char *kwlist[] = { "stroke", NULL };
600 
601     PyArg_ParseTupleAndKeywords(args, kwargs, "O:remove_stroke", kwlist, &stroke);
602 
603     if (PyInt_Check(stroke))
604         stroke_id = PyInt_AsLong(stroke);
605     else if (PyObject_IsInstance(stroke, (PyObject *) &PyGimpVectorsStroke_Type))
606         stroke_id = ((PyGimpVectorsStroke *) stroke)->stroke;
607     else  {
608         PyErr_SetString(PyExc_TypeError, "stroke must be a gimp.VectorsBezierStroke object or an Integer");
609         return NULL;
610     }
611 
612     gimp_vectors_remove_stroke(self->ID, stroke_id);
613 
614     Py_INCREF(Py_None);
615     return Py_None;
616 }
617 
618 static PyObject *
vectors_to_selection(PyGimpVectors * self,PyObject * args,PyObject * kwargs)619 vectors_to_selection(PyGimpVectors *self, PyObject *args, PyObject *kwargs)
620 {
621     GimpChannelOps operation = GIMP_CHANNEL_OP_REPLACE;
622     gboolean antialias = TRUE, feather = FALSE;
623     double feather_radius_x = 0.0, feather_radius_y = 0.0;
624 
625     static char *kwlist[] = { "operation", "antialias", "feather",
626                               "feather_radius_x", "feather_radius_y", NULL };
627 
628     if (!PyArg_ParseTupleAndKeywords(args, kwargs,
629                                      "|iiidd:to_selection", kwlist,
630                                      &operation, &antialias, &feather,
631                                      &feather_radius_x, &feather_radius_y))
632         return NULL;
633 
634     gimp_context_push();
635     gimp_context_set_antialias(antialias);
636     gimp_context_set_feather(feather);
637     gimp_context_set_feather_radius(feather_radius_x, feather_radius_y);
638     gimp_image_select_item(gimp_item_get_image(self->ID), operation, self->ID);
639     gimp_context_pop();
640 
641     Py_INCREF(Py_None);
642     return Py_None;
643 }
644 
645 static PyObject *
vectors_parasite_find(PyGimpVectors * self,PyObject * args)646 vectors_parasite_find(PyGimpVectors *self, PyObject *args)
647 {
648     char *name;
649 
650     if (!PyArg_ParseTuple(args, "s:parasite_find", &name))
651         return NULL;
652 
653     return pygimp_parasite_new(gimp_item_get_parasite(self->ID, name));
654 }
655 
656 static PyObject *
vectors_parasite_attach(PyGimpVectors * self,PyObject * args)657 vectors_parasite_attach(PyGimpVectors *self, PyObject *args)
658 {
659     PyGimpParasite *parasite;
660 
661     if (!PyArg_ParseTuple(args, "O!:parasite_attach", &PyGimpParasite_Type,
662                           &parasite))
663         return NULL;
664 
665     if (!gimp_item_attach_parasite(self->ID, parasite->para)) {
666         PyErr_Format(pygimp_error,
667                      "could not attach parasite '%s' to vectors (ID %d)",
668                      parasite->para->name, self->ID);
669         return NULL;
670     }
671 
672     Py_INCREF(Py_None);
673     return Py_None;
674 }
675 
676 static PyObject *
vectors_parasite_detach(PyGimpVectors * self,PyObject * args)677 vectors_parasite_detach(PyGimpVectors *self, PyObject *args)
678 {
679     char *name;
680 
681     if (!PyArg_ParseTuple(args, "s:parasite_detach", &name))
682         return NULL;
683 
684     if (!gimp_item_detach_parasite(self->ID, name)) {
685         PyErr_Format(pygimp_error,
686                      "could not detach parasite '%s' from vectors (ID %d)",
687                      name, self->ID);
688         return NULL;
689     }
690 
691     Py_INCREF(Py_None);
692     return Py_None;
693 }
694 
695 static PyObject *
vectors_parasite_list(PyGimpVectors * self)696 vectors_parasite_list(PyGimpVectors *self)
697 {
698     gint num_parasites;
699     gchar **parasites;
700     PyObject *ret;
701     gint i;
702 
703     parasites = gimp_item_get_parasite_list(self->ID, &num_parasites);
704 
705     ret = PyTuple_New(num_parasites);
706 
707     for (i = 0; i < num_parasites; i++)
708         PyTuple_SetItem(ret, i, PyString_FromString(parasites[i]));
709 
710     g_strfreev(parasites);
711     return ret;
712 }
713 
714 static PyMethodDef vectors_methods[] = {
715     { "remove_stroke",
716       (PyCFunction)vectors_remove_stroke,
717       METH_VARARGS | METH_KEYWORDS },
718     { "to_selection",
719       (PyCFunction)vectors_to_selection,
720       METH_VARARGS | METH_KEYWORDS },
721     { "parasite_find",
722       (PyCFunction)vectors_parasite_find,
723       METH_VARARGS },
724     { "parasite_attach",
725       (PyCFunction)vectors_parasite_attach,
726       METH_VARARGS },
727     { "parasite_detach",
728       (PyCFunction)vectors_parasite_detach,
729       METH_VARARGS },
730     { "parasite_list",
731       (PyCFunction)vectors_parasite_list,
732       METH_NOARGS },
733     { NULL, NULL, 0 }
734 };
735 
736 static PyObject *
vectors_get_image(PyGimpVectors * self,void * closure)737 vectors_get_image(PyGimpVectors *self, void *closure)
738 {
739     return pygimp_image_new(gimp_item_get_image(self->ID));
740 }
741 
742 static PyObject *
vectors_get_ID(PyGimpVectors * self,void * closure)743 vectors_get_ID(PyGimpVectors *self, void *closure)
744 {
745     return PyInt_FromLong(self->ID);
746 }
747 
748 static PyObject *
vectors_get_name(PyGimpVectors * self,void * closure)749 vectors_get_name(PyGimpVectors *self, void *closure)
750 {
751     return PyString_FromString(gimp_item_get_name(self->ID));
752 }
753 
754 static int
vectors_set_name(PyGimpVectors * self,PyObject * value,void * closure)755 vectors_set_name(PyGimpVectors *self, PyObject *value, void *closure)
756 {
757     if (value == NULL) {
758         PyErr_SetString(PyExc_TypeError, "cannot delete name");
759         return -1;
760     }
761 
762     if (!PyString_Check(value) && !PyUnicode_Check(value)) {
763         PyErr_SetString(PyExc_TypeError, "type mismatch");
764         return -1;
765     }
766 
767     gimp_item_set_name(self->ID, PyString_AsString(value));
768 
769     return 0;
770 }
771 
772 static PyObject *
vectors_get_visible(PyGimpVectors * self,void * closure)773 vectors_get_visible(PyGimpVectors *self, void *closure)
774 {
775     return PyBool_FromLong(gimp_item_get_visible(self->ID));
776 }
777 
778 static int
vectors_set_visible(PyGimpVectors * self,PyObject * value,void * closure)779 vectors_set_visible(PyGimpVectors *self, PyObject *value, void *closure)
780 {
781     if (value == NULL) {
782         PyErr_SetString(PyExc_TypeError, "cannot delete visible");
783         return -1;
784     }
785 
786     if (!PyInt_Check(value)) {
787         PyErr_SetString(PyExc_TypeError, "type mismatch");
788         return -1;
789     }
790 
791     gimp_item_set_visible(self->ID, PyInt_AsLong(value));
792 
793     return 0;
794 }
795 
796 static PyObject *
vectors_get_linked(PyGimpVectors * self,void * closure)797 vectors_get_linked(PyGimpVectors *self, void *closure)
798 {
799     return PyBool_FromLong(gimp_item_get_linked(self->ID));
800 }
801 
802 static int
vectors_set_linked(PyGimpVectors * self,PyObject * value,void * closure)803 vectors_set_linked(PyGimpVectors *self, PyObject *value, void *closure)
804 {
805     if (value == NULL) {
806         PyErr_SetString(PyExc_TypeError, "cannot delete linked");
807         return -1;
808     }
809 
810     if (!PyInt_Check(value)) {
811         PyErr_SetString(PyExc_TypeError, "type mismatch");
812         return -1;
813     }
814 
815     gimp_item_set_linked(self->ID, PyInt_AsLong(value));
816 
817     return 0;
818 }
819 
820 static PyObject *
vectors_get_tattoo(PyGimpVectors * self,void * closure)821 vectors_get_tattoo(PyGimpVectors *self, void *closure)
822 {
823     return PyInt_FromLong(gimp_item_get_tattoo(self->ID));
824 }
825 
826 static int
vectors_set_tattoo(PyGimpVectors * self,PyObject * value,void * closure)827 vectors_set_tattoo(PyGimpVectors *self, PyObject *value, void *closure)
828 {
829     if (value == NULL) {
830         PyErr_SetString(PyExc_TypeError, "cannot delete tattoo");
831         return -1;
832     }
833 
834     if (!PyInt_Check(value)) {
835         PyErr_SetString(PyExc_TypeError, "type mismatch");
836         return -1;
837     }
838 
839     gimp_item_set_tattoo(self->ID, PyInt_AsLong(value));
840 
841     return 0;
842 }
843 
844 static PyObject *
vectors_get_strokes(PyGimpVectors * self,void * closure)845 vectors_get_strokes(PyGimpVectors *self, void *closure)
846 {
847     int *strokes;
848     int i, num_strokes;
849     PyObject *ret;
850 
851     strokes = gimp_vectors_get_strokes(self->ID, &num_strokes);
852 
853     ret = PyList_New(num_strokes);
854     if (ret == NULL)
855         return NULL;
856 
857     for (i = 0; i < num_strokes; i++)
858         PyList_SetItem(ret, i, vectors_bezier_stroke_new(self, strokes[i]));
859 
860     g_free(strokes);
861 
862     return ret;
863 }
864 
865 static PyGetSetDef vectors_getsets[] = {
866     { "ID", (getter)vectors_get_ID, (setter)0 },
867     { "image", (getter)vectors_get_image, (setter)0 },
868     { "name", (getter)vectors_get_name, (setter)vectors_set_name },
869     { "visible", (getter)vectors_get_visible, (setter)vectors_set_visible },
870     { "linked", (getter)vectors_get_linked, (setter)vectors_set_linked },
871     { "tattoo", (getter)vectors_get_tattoo, (setter)vectors_set_tattoo },
872     { "strokes", (getter)vectors_get_strokes, (setter)0 },
873     { NULL, (getter)0, (setter)0 }
874 };
875 
876 static void
vectors_dealloc(PyGimpVectors * self)877 vectors_dealloc(PyGimpVectors *self)
878 {
879     PyObject_DEL(self);
880 }
881 
882 static PyObject *
vectors_repr(PyGimpVectors * self)883 vectors_repr(PyGimpVectors *self)
884 {
885     PyObject *s;
886     char *name;
887 
888     name = gimp_item_get_name(self->ID);
889     s = PyString_FromFormat("<gimp.Vectors '%s'>", name ? name : "(null)");
890     g_free(name);
891 
892     return s;
893 }
894 
895 static int
vectors_cmp(PyGimpVectors * self,PyGimpVectors * other)896 vectors_cmp(PyGimpVectors *self, PyGimpVectors *other)
897 {
898     if (self->ID == other->ID)
899         return 0;
900     if (self->ID > other->ID)
901         return -1;
902     return 1;
903 }
904 
905 static int
vectors_init(PyGimpVectors * self,PyObject * args,PyObject * kwargs)906 vectors_init(PyGimpVectors *self, PyObject *args, PyObject *kwargs)
907 {
908     PyGimpImage *img;
909     char *name;
910 
911     static char *kwlist[] = { "image", "name", NULL };
912 
913     if (!PyArg_ParseTupleAndKeywords(args, kwargs,
914                                      "O!s:gimp.Vectors.__init__",
915                                      kwlist,
916                                      &PyGimpImage_Type, &img, &name))
917         return -1;
918 
919     self->ID = gimp_vectors_new(img->ID, name);
920 
921     if (self->ID < 0) {
922         PyErr_Format(pygimp_error,
923                      "could not create vectors '%s' on image (ID %d)",
924                      name, img->ID);
925         return -1;
926     }
927 
928     return 0;
929 }
930 
931 PyTypeObject PyGimpVectors_Type = {
932     PyObject_HEAD_INIT(NULL)
933     0,                                  /* ob_size */
934     "gimp.Vectors",                     /* tp_name */
935     sizeof(PyGimpVectors),              /* tp_basicsize */
936     0,                                  /* tp_itemsize */
937     /* methods */
938     (destructor)vectors_dealloc,        /* tp_dealloc */
939     (printfunc)0,                       /* tp_print */
940     (getattrfunc)0,                     /* tp_getattr */
941     (setattrfunc)0,                     /* tp_setattr */
942     (cmpfunc)vectors_cmp,               /* tp_compare */
943     (reprfunc)vectors_repr,             /* tp_repr */
944     0,                                  /* tp_as_number */
945     0,                                  /* tp_as_sequence */
946     0,                                  /* tp_as_mapping */
947     (hashfunc)0,                        /* tp_hash */
948     (ternaryfunc)0,                     /* tp_call */
949     (reprfunc)0,                        /* tp_str */
950     (getattrofunc)0,                    /* tp_getattro */
951     (setattrofunc)0,                    /* tp_setattro */
952     0,                                  /* tp_as_buffer */
953     Py_TPFLAGS_DEFAULT,                 /* tp_flags */
954     NULL, /* Documentation string */
955     (traverseproc)0,                    /* tp_traverse */
956     (inquiry)0,                         /* tp_clear */
957     (richcmpfunc)0,                     /* tp_richcompare */
958     0,                                  /* tp_weaklistoffset */
959     (getiterfunc)0,                     /* tp_iter */
960     (iternextfunc)0,                    /* tp_iternext */
961     vectors_methods,                    /* tp_methods */
962     0,                                  /* tp_members */
963     vectors_getsets,                    /* tp_getset */
964     &PyGimpItem_Type,                   /* tp_base */
965     (PyObject *)0,                      /* tp_dict */
966     0,                                  /* tp_descr_get */
967     0,                                  /* tp_descr_set */
968     0,                                  /* tp_dictoffset */
969     (initproc)vectors_init,             /* tp_init */
970     (allocfunc)0,                       /* tp_alloc */
971     (newfunc)0,                         /* tp_new */
972 };
973 
974 PyObject *
pygimp_vectors_new(gint32 ID)975 pygimp_vectors_new(gint32 ID)
976 {
977     PyGimpVectors *self;
978 
979     if (!gimp_item_is_valid(ID)) {
980         Py_INCREF(Py_None);
981         return Py_None;
982     }
983 
984     self = PyObject_NEW(PyGimpVectors, &PyGimpVectors_Type);
985 
986     if (self == NULL)
987         return NULL;
988 
989     self->ID = ID;
990 
991     return (PyObject *)self;
992 }
993