=IDProperties bugfix=
[blender-staging.git] / source / blender / python / api2_2x / IDProp.c
1 /**
2  * $Id: IDProp.c
3  *
4  * ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version. The Blender
10  * Foundation also sells licenses for use in proprietary software under
11  * the Blender License.  See http://www.blender.org/BL/ for information
12  * about this.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22  *
23  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
24  * All rights reserved.
25  *
26  * Contributor(s): Joseph Eagar
27  *
28  * ***** END GPL/BL DUAL LICENSE BLOCK *****
29  */
30  
31 #include "DNA_ID.h"
32
33 #include "BKE_idprop.h"
34
35 #include "IDProp.h"
36 #include "gen_utils.h"
37
38 #include "MEM_guardedalloc.h"
39
40 #define BSTR_EQ(a, b)   (*(a) == *(b) && !strcmp(a, b))
41
42 /*** Function to wrap ID properties ***/
43 PyObject *BPy_Wrap_IDProperty(ID *id, IDProperty *prop, IDProperty *parent);
44
45 extern PyTypeObject IDArray_Type;
46
47 /*********************** ID Property Main Wrapper Stuff ***************/
48 void IDProperty_dealloc(BPy_IDProperty *self)
49 {
50         if (self->data_wrap) {
51                 Py_XDECREF(self->data_wrap);
52         }
53
54         /*if (self->destroy) {
55                 IDP_FreeProperty(self->prop);
56                 MEM_freeN(self->prop);
57         }*/
58
59         PyObject_DEL(self);
60 }
61
62 PyObject *IDProperty_repr(BPy_IDProperty *self)
63 {
64         return Py_BuildValue("s", "(ID Property)");
65 }
66
67 extern PyTypeObject IDGroup_Type;
68
69 PyObject *BPy_IDProperty_getattr(BPy_IDProperty *self, char *name)
70 {
71         if (BSTR_EQ(name, "data")) {
72                 switch (self->prop->type) {
73                         case IDP_STRING:
74                                 return Py_BuildValue("s", self->prop->data.pointer);
75                         case IDP_INT:
76                                 return Py_BuildValue("i", self->prop->data.val);
77                         case IDP_FLOAT:
78                                 return Py_BuildValue("f", *(float*)(&self->prop->data.val));
79                         case IDP_GROUP:
80                                 /*blegh*/
81                                 if (self->data_wrap) {
82                                         Py_XINCREF(self->data_wrap);
83                                         return self->data_wrap;
84                                 } else {
85                                         BPy_IDProperty *group = PyObject_New(BPy_IDProperty, &IDGroup_Type);
86                                         group->id = self->id;
87                                         group->data_wrap = NULL;
88                                         group->prop = self->prop;
89                                         Py_XINCREF(group);
90                                         self->data_wrap = (PyObject*) group;
91                                         return (PyObject*) group;
92                                 }
93                         case IDP_ARRAY:
94                                 if (self->data_wrap) {
95                                         Py_XINCREF(self->data_wrap);
96                                         return self->data_wrap;
97                                 } else {
98                                         BPy_IDProperty *array = PyObject_New(BPy_IDProperty, &IDArray_Type);
99                                         array->id = self->id;
100                                         array->data_wrap = NULL;
101                                         array->prop = self->prop;
102                                         Py_XINCREF(array);
103                                         self->data_wrap = (PyObject*) array;
104                                         return (PyObject*) array;
105                                 }
106                         case IDP_MATRIX:
107                         case IDP_VECTOR:
108                                 break;
109                 }
110         } else if (BSTR_EQ(name, "name")) {
111                 return Py_BuildValue("s", self->prop->name);
112         } else if (BSTR_EQ(name, "type")) {
113                 return Py_BuildValue("i", self->prop->type);
114         //} else if (BSTR_EQ(name, "object")) {
115                 /*hrm the idea is here is to return the wrapped ID object. how the hell
116                   do I do that? eek! */
117         } else if (BSTR_EQ(name, "__members__"))
118                 return Py_BuildValue("[s, s, s]", "data", "name", "type");
119
120         return NULL;
121 }
122
123 int BPy_IDProperty_setattr(BPy_IDProperty *self, char *name, PyObject *val)
124 {
125         if (BSTR_EQ(name, "type"))
126                 return EXPP_ReturnIntError(PyExc_TypeError, "attempt to set read-only attribute!");
127         else if (BSTR_EQ(name, "name")) {
128                 char *st;
129                 if (!PyString_Check(val))
130                         return EXPP_ReturnIntError(PyExc_TypeError, "expected a string!");
131
132                 st = PyString_AsString(val);
133                 if (strlen(st) >= MAX_IDPROP_NAME)
134                         return EXPP_ReturnIntError(PyExc_TypeError, "string length cannot exceed 31 characters!");
135
136                 strcpy(self->prop->name, st);
137                 return 0;
138         } else if (BSTR_EQ(name, "data")) {
139                 switch (self->prop->type) {
140                         case IDP_STRING:
141                         {
142                                 char *st;
143                                 if (!PyString_Check(val))
144                                         return EXPP_ReturnIntError(PyExc_TypeError, "expected a string!");
145
146                                 st = PyString_AsString(val);
147                                 IDP_ResizeArray(self->prop, strlen(st)+1);
148                                 strcpy(self->prop->data.pointer, st);
149                                 return 0;
150                         }
151
152                         case IDP_INT:
153                         {
154                                 int ival;
155                                 if (!PyNumber_Check(val))
156                                         return EXPP_ReturnIntError(PyExc_TypeError, "expected an int!");
157                                 val = PyNumber_Int(val);
158                                 if (!val)
159                                         return EXPP_ReturnIntError(PyExc_TypeError, "expected an int!");
160                                 ival = (int) PyInt_AsLong(val);
161                                 self->prop->data.val = ival;
162                                 Py_XDECREF(val);
163                                 break;
164                         }
165                         case IDP_FLOAT:
166                         {
167                                 float fval;
168                                 if (!PyNumber_Check(val))
169                                         return EXPP_ReturnIntError(PyExc_TypeError, "expected a float!");
170                                 val = PyNumber_Float(val);
171                                 if (!val)
172                                         return EXPP_ReturnIntError(PyExc_TypeError, "expected a float!");
173                                 fval = (float) PyFloat_AsDouble(val);
174                                 *(float*)&self->prop->data.val = fval;
175                                 Py_XDECREF(val);
176                                 break;
177                         }
178
179                         default:
180                                 return EXPP_ReturnIntError(PyExc_AttributeError, "attempt to set read-only attribute!");
181                 }
182                 return 0;
183         }
184
185         return EXPP_ReturnIntError(PyExc_TypeError, "invalid attribute!");
186 }
187
188 int BPy_IDProperty_Map_Len(BPy_IDProperty *self)
189 {
190         if (self->prop->type != IDP_GROUP)
191                 return EXPP_ReturnIntError( PyExc_TypeError,
192                         "len() of unsized object");
193                         
194         return self->prop->len;
195 }
196
197 PyObject *BPy_IDProperty_Map_GetItem(BPy_IDProperty *self, PyObject *item)
198 {
199         IDProperty *loop;
200         char *st;
201         
202         if (self->prop->type  != IDP_GROUP)
203                 return EXPP_ReturnPyObjError( PyExc_TypeError,
204                         "unsubscriptable object");
205                         
206         if (!PyString_Check(item)) 
207                 return EXPP_ReturnPyObjError( PyExc_TypeError,
208                         "only strings are allowed as keys of ID properties");
209         
210         st = PyString_AsString(item);
211         for (loop=self->prop->data.group.first; loop; loop=loop->next) {
212                 if (BSTR_EQ(loop->name, st)) return BPy_Wrap_IDProperty(self->id, loop, self->prop);
213         }
214         return EXPP_ReturnPyObjError( PyExc_KeyError,
215                 "key not in subgroup dict");
216 }
217
218 /*returns NULL on success, error string on failure*/
219 char *BPy_IDProperty_Map_ValidateAndCreate(char *name, IDProperty *group, PyObject *ob)
220 {
221         IDProperty *prop = NULL;
222         IDPropertyTemplate val;
223         
224         if (PyFloat_Check(ob)) {
225                 val.f = (float) PyFloat_AsDouble(ob);
226                 prop = IDP_New(IDP_FLOAT, val, name);
227         } else if (PyInt_Check(ob)) {
228                 val.i = (int) PyInt_AsLong(ob);
229                 prop = IDP_New(IDP_INT, val, name);
230         } else if (PyString_Check(ob)) {
231                 val.str = PyString_AsString(ob);
232                 prop = IDP_New(IDP_STRING, val, name);
233         } else if (PySequence_Check(ob)) {
234                 PyObject *item;
235                 int i;
236                 
237                 /*validate sequence and derive type.
238                 we assume IDP_INT unless we hit a float
239                 number; then we assume it's */
240                 val.array.type = IDP_INT;
241                 val.array.len = PySequence_Length(ob);
242                 for (i=0; i<val.array.len; i++) {
243                         item = PySequence_GetItem(ob, i);
244                         if (PyFloat_Check(item)) val.array.type = IDP_FLOAT;
245                         else if (!PyInt_Check(item)) return "only floats and ints are allowed in ID property arrays";
246                         Py_XDECREF(item);
247                 }
248                 
249                 prop = IDP_New(IDP_ARRAY, val, name);
250                 for (i=0; i<val.array.len; i++) {
251                         item = PySequence_GetItem(ob, i);
252                         Py_XDECREF(item);
253                         if (val.array.type == IDP_INT) {
254                                 item = PyNumber_Int(item);
255                                 ((int*)prop->data.pointer)[i] = (int)PyInt_AsLong(item);
256                         } else {
257                                 item = PyNumber_Float(item);
258                                 ((float*)prop->data.pointer)[i] = (float)PyFloat_AsDouble(item);
259                         }
260                         Py_XDECREF(item);
261                 }
262         } else if (PyMapping_Check(ob)) {
263                 PyObject *keys, *vals, *key, *pval;
264                 int i, len;
265                 /*yay! we get into recursive stuff now!*/
266                 keys = PyMapping_Keys(ob);
267                 vals = PyMapping_Values(ob);
268                 
269                 /*we allocate the group first; if we hit any invalid data,
270                   we can delete it easily enough.*/
271                 prop = IDP_New(IDP_GROUP, val, name);
272                 len = PyMapping_Length(ob);
273                 for (i=0; i<len; i++) {
274                         key = PySequence_GetItem(keys, i);
275                         pval = PySequence_GetItem(vals, i);
276                         if (!PyString_Check(key)) {
277                                 IDP_FreeProperty(prop);
278                                 MEM_freeN(prop);
279                                 Py_XDECREF(key);
280                                 Py_XDECREF(pval);
281                                 return "invalid element in subgroup dict template!";
282                         }
283                         if (BPy_IDProperty_Map_ValidateAndCreate(PyString_AsString(key), prop, pval)) {
284                                 IDP_FreeProperty(prop);
285                                 MEM_freeN(prop);
286                                 Py_XDECREF(key);
287                                 Py_XDECREF(pval);
288                                 return "invalid element in subgroup dict template!";
289                         }
290                         Py_XDECREF(key);
291                         Py_XDECREF(pval);
292                 }
293                 Py_XDECREF(keys);
294                 Py_XDECREF(vals);
295         }
296         
297         if (IDP_GetPropertyFromGroup(group, prop->name) == NULL) {
298                 IDP_RemFromGroup(group, IDP_GetPropertyFromGroup(group, prop->name));
299         }
300
301         IDP_AddToGroup(group, prop);
302         return NULL;
303 }
304
305 int BPy_IDProperty_Map_SetItem(BPy_IDProperty *self, PyObject *key, PyObject *val)
306 {
307         char *err;
308         
309         if (self->prop->type  != IDP_GROUP)
310                 return EXPP_ReturnIntError( PyExc_TypeError,
311                         "unsubscriptable object");
312                         
313         if (!PyString_Check(key))
314                 return EXPP_ReturnIntError( PyExc_TypeError,
315                    "only strings are allowed as subgroup keys" );
316         
317         err = BPy_IDProperty_Map_ValidateAndCreate(PyString_AsString(key), self->prop, val);
318         if (err) return EXPP_ReturnIntError( PyExc_RuntimeError, err );
319         
320         return 0;
321 }
322
323 PyMappingMethods BPy_IDProperty_Mapping = {
324         (inquiry)BPy_IDProperty_Map_Len,                        /*inquiry mp_length */
325         (binaryfunc)BPy_IDProperty_Map_GetItem,         /*binaryfunc mp_subscript */
326         (objobjargproc)BPy_IDProperty_Map_SetItem,      /*objobjargproc mp_ass_subscript */
327 };
328
329 PyTypeObject IDProperty_Type = {
330         PyObject_HEAD_INIT( NULL )  /* required py macro */
331         0,                          /* ob_size */
332         /*  For printing, in format "<module>.<name>" */
333         "Blender IDProperty",           /* char *tp_name; */
334         sizeof( BPy_IDProperty ),       /* int tp_basicsize; */
335         0,                          /* tp_itemsize;  For allocation */
336
337         /* Methods to implement standard operations */
338
339         ( destructor ) IDProperty_dealloc,/* destructor tp_dealloc; */
340         NULL,                       /* printfunc tp_print; */
341         (getattrfunc) BPy_IDProperty_getattr,     /* getattrfunc tp_getattr; */
342         (setattrfunc) BPy_IDProperty_setattr,     /* setattrfunc tp_setattr; */
343         NULL,                       /* cmpfunc tp_compare; */
344         ( reprfunc ) IDProperty_repr,     /* reprfunc tp_repr; */
345
346         /* Method suites for standard classes */
347
348         NULL,                       /* PyNumberMethods *tp_as_number; */
349         NULL,                                   /* PySequenceMethods *tp_as_sequence; */
350         &BPy_IDProperty_Mapping,     /* PyMappingMethods *tp_as_mapping; */
351
352         /* More standard operations (here for binary compatibility) */
353
354         NULL,                       /* hashfunc tp_hash; */
355         NULL,                       /* ternaryfunc tp_call; */
356         NULL,                       /* reprfunc tp_str; */
357         NULL,                       /* getattrofunc tp_getattro; */
358         NULL,                       /* setattrofunc tp_setattro; */
359
360         /* Functions to access object as input/output buffer */
361         NULL,                       /* PyBufferProcs *tp_as_buffer; */
362
363   /*** Flags to define presence of optional/expanded features ***/
364         Py_TPFLAGS_DEFAULT,         /* long tp_flags; */
365
366         NULL,                       /*  char *tp_doc;  Documentation string */
367   /*** Assigned meaning in release 2.0 ***/
368         /* call function for all accessible objects */
369         NULL,                       /* traverseproc tp_traverse; */
370
371         /* delete references to contained objects */
372         NULL,                       /* inquiry tp_clear; */
373
374   /***  Assigned meaning in release 2.1 ***/
375   /*** rich comparisons ***/
376         NULL,                       /* richcmpfunc tp_richcompare; */
377
378   /***  weak reference enabler ***/
379         0,                          /* long tp_weaklistoffset; */
380
381   /*** Added in release 2.2 ***/
382         /*   Iterators */
383         NULL,                       /* getiterfunc tp_iter; */
384         NULL,                       /* iternextfunc tp_iternext; */
385 };
386
387 /*********** Main wrapping function *******/
388 PyObject *BPy_Wrap_IDProperty(ID *id, IDProperty *prop, IDProperty *parent)
389 {
390         BPy_IDProperty *wrap = PyObject_New(BPy_IDProperty, &IDProperty_Type);
391         wrap->prop = prop;
392         wrap->parent = parent;
393         wrap->id = id;
394         wrap->data_wrap = NULL;
395         //wrap->destroy = 0;
396         return (PyObject*) wrap;
397 }
398
399
400 /********Array Wrapper********/
401
402 void IDArray_dealloc(void *self)
403 {
404         PyObject_DEL(self);
405 }
406
407 PyObject *IDArray_repr(BPy_IDArray *self)
408 {
409         return Py_BuildValue("s", "(ID Array)");
410 }
411
412 PyObject *BPy_IDArray_getattr(BPy_IDArray *self, char *name)
413 {
414         if (BSTR_EQ(name, "len")) return Py_BuildValue("i", self->prop->len);
415         else if (BSTR_EQ(name, "type")) return Py_BuildValue("i", self->prop->subtype);
416         else return NULL;
417 }
418
419 int BPy_IDArray_setattr(BPy_IDArray *self, PyObject *val, char *name)
420 {
421         return -1;
422 }
423
424 int BPy_IDArray_Len(BPy_IDArray *self)
425 {
426         return self->prop->len;
427 }
428
429 PyObject *BPy_IDArray_GetItem(BPy_IDArray *self, int index)
430 {
431         if (index < 0 || index >= self->prop->len)
432                 return EXPP_ReturnPyObjError( PyExc_IndexError,
433                                 "index out of range!");
434
435         switch (self->prop->subtype) {
436                 case IDP_FLOAT:
437                         return Py_BuildValue("f", ((float*)self->prop->data.pointer)[index]);
438                         break;
439                 case IDP_INT:
440                         return Py_BuildValue("i", ((int*)self->prop->data.pointer)[index]);
441                         break;
442                 /*case IDP_LISTARRAY:
443                         return BPy_Wrap_IDProperty(self->id, ((IDProperty**)self->prop->data.array)[index]);
444                         break;*/
445         }
446                 return EXPP_ReturnPyObjError( PyExc_RuntimeError,
447                                 "invalid/corrupt array type!");
448 }
449
450 int BPy_IDArray_SetItem(BPy_IDArray *self, int index, PyObject *val)
451 {
452         int i;
453         float f;
454
455         if (index < 0 || index >= self->prop->len)
456                 return EXPP_ReturnIntError( PyExc_RuntimeError,
457                                 "index out of range!");
458
459         switch (self->prop->subtype) {
460                 case IDP_FLOAT:
461                         if (!PyNumber_Check(val)) return EXPP_ReturnIntError( PyExc_TypeError,
462                                 "expected a float");
463                         val = PyNumber_Float(val);
464                         if (!val) return EXPP_ReturnIntError( PyExc_TypeError,
465                                 "expected a float");
466
467                         f = (float) PyFloat_AsDouble(val);
468                         ((float*)self->prop->data.pointer)[index] = f;
469                         Py_XDECREF(val);
470                         break;
471                 case IDP_INT:
472                         if (!PyNumber_Check(val)) return EXPP_ReturnIntError( PyExc_TypeError,
473                                 "expected an int");
474                         val = PyNumber_Int(val);
475                         if (!val) return EXPP_ReturnIntError( PyExc_TypeError,
476                                 "expected an int");
477
478                         i = (int) PyInt_AsLong(val);
479                         ((int*)self->prop->data.pointer)[index] = i;
480                         Py_XDECREF(val);
481                         break;
482                 /*case IDP_LISTARRAY:
483                         if (!PyObject_TypeCheck(val, &IDProperty_Type))
484                                 return EXPP_ReturnIntError( PyExc_TypeError,
485                                 "expected an IDProperty");
486
487                         if (((IDProperty**)self->prop->data.array)[index]) {
488                                 IDP_FreeProperty(((IDProperty**)self->prop->data.array)[index]);
489                                 MEM_freeN(((IDProperty**)self->prop->data.array)[index]);
490                         }
491                         ((IDProperty**)self->prop->data.array)[index] = ((BPy_IDProperty*);
492                         break;*/
493         }
494         return 0;
495 }
496
497 static PySequenceMethods BPy_IDArray_Seq = {
498         (inquiry) BPy_IDArray_Len,                      /* inquiry sq_length */
499         0,                                                                      /* binaryfunc sq_concat */
500         0,                                                                      /* intargfunc sq_repeat */
501         (intargfunc)BPy_IDArray_GetItem,        /* intargfunc sq_item */
502         0,                                                                      /* intintargfunc sq_slice */
503         (intobjargproc)BPy_IDArray_SetItem,     /* intobjargproc sq_ass_item */
504         0,                                                                      /* intintobjargproc sq_ass_slice */
505         0,                                                                      /* objobjproc sq_contains */
506                                 /* Added in release 2.0 */
507         0,                                                                      /* binaryfunc sq_inplace_concat */
508         0,                                                                      /* intargfunc sq_inplace_repeat */
509 };
510
511 PyTypeObject IDArray_Type = {
512         PyObject_HEAD_INIT( NULL )  /* required py macro */
513         0,                          /* ob_size */
514         /*  For printing, in format "<module>.<name>" */
515         "Blender IDArray",           /* char *tp_name; */
516         sizeof( BPy_IDArray ),       /* int tp_basicsize; */
517         0,                          /* tp_itemsize;  For allocation */
518
519         /* Methods to implement standard operations */
520
521         ( destructor ) IDArray_dealloc,/* destructor tp_dealloc; */
522         NULL,                       /* printfunc tp_print; */
523         ( getattrfunc ) BPy_IDArray_getattr,     /* getattrfunc tp_getattr; */
524         ( setattrfunc ) BPy_IDArray_setattr,     /* setattrfunc tp_setattr; */
525         NULL,                       /* cmpfunc tp_compare; */
526         ( reprfunc ) IDArray_repr,     /* reprfunc tp_repr; */
527
528         /* Method suites for standard classes */
529
530         NULL,                       /* PyNumberMethods *tp_as_number; */
531         &BPy_IDArray_Seq,                       /* PySequenceMethods *tp_as_sequence; */
532         NULL,                       /* PyMappingMethods *tp_as_mapping; */
533
534         /* More standard operations (here for binary compatibility) */
535
536         NULL,                       /* hashfunc tp_hash; */
537         NULL,                       /* ternaryfunc tp_call; */
538         NULL,                       /* reprfunc tp_str; */
539         NULL,                       /* getattrofunc tp_getattro; */
540         NULL,                       /* setattrofunc tp_setattro; */
541
542         /* Functions to access object as input/output buffer */
543         NULL,                       /* PyBufferProcs *tp_as_buffer; */
544
545   /*** Flags to define presence of optional/expanded features ***/
546         Py_TPFLAGS_DEFAULT,         /* long tp_flags; */
547
548         NULL,                       /*  char *tp_doc;  Documentation string */
549   /*** Assigned meaning in release 2.0 ***/
550         /* call function for all accessible objects */
551         NULL,                       /* traverseproc tp_traverse; */
552
553         /* delete references to contained objects */
554         NULL,                       /* inquiry tp_clear; */
555
556   /***  Assigned meaning in release 2.1 ***/
557   /*** rich comparisons ***/
558         NULL,                       /* richcmpfunc tp_richcompare; */
559
560   /***  weak reference enabler ***/
561         0,                          /* long tp_weaklistoffset; */
562
563   /*** Added in release 2.2 ***/
564         /*   Iterators */
565         NULL,                       /* getiterfunc tp_iter; */
566         NULL,                       /* iternextfunc tp_iternext; */
567
568   /*** Attribute descriptor and subclassing stuff ***/
569         NULL,                       /* struct PyMethodDef *tp_methods; */
570         NULL,                       /* struct PyMemberDef *tp_members; */
571         NULL,       /* struct PyGetSetDef *tp_getset; */
572         NULL,                       /* struct _typeobject *tp_base; */
573         NULL,                       /* PyObject *tp_dict; */
574         NULL,                       /* descrgetfunc tp_descr_get; */
575         NULL,                       /* descrsetfunc tp_descr_set; */
576         0,                          /* long tp_dictoffset; */
577         NULL,                       /* initproc tp_init; */
578         NULL,                       /* allocfunc tp_alloc; */
579         NULL,                       /* newfunc tp_new; */
580         /*  Low-level free-memory routine */
581         NULL,                       /* freefunc tp_free;  */
582         /* For PyObject_IS_GC */
583         NULL,                       /* inquiry tp_is_gc;  */
584         NULL,                       /* PyObject *tp_bases; */
585         /* method resolution order */
586         NULL,                       /* PyObject *tp_mro;  */
587         NULL,                       /* PyObject *tp_cache; */
588         NULL,                       /* PyObject *tp_subclasses; */
589         NULL,                       /* PyObject *tp_weaklist; */
590         NULL
591 };
592
593 /*********** ID Property Group iterator ********/
594 void IDGroup_Iter_dealloc(void *self)
595 {
596         PyObject_DEL(self);
597 }
598
599 PyObject *IDGroup_Iter_repr(BPy_IDGroup_Iter *self)
600 {
601         return Py_BuildValue("s", "(ID Property Group)");
602 }
603
604 PyObject *BPy_IDGroup_Iter_getattr(BPy_IDGroup_Iter *self, char *name)
605 {
606         return Py_BuildValue("(s)", "None!! Not implemented!!");
607 }
608
609 PyObject *BPy_IDGroup_Iter_setattr(BPy_IDGroup_Iter *self, PyObject *val, char *name)
610 {
611         return 0;
612 }
613
614 PyObject *BPy_Group_Iter_Next(BPy_IDGroup_Iter *self)
615 {
616         IDProperty *cur=NULL;
617
618         if (self->cur) {
619                 cur = self->cur;
620                 self->cur = self->cur->next;
621                 return BPy_Wrap_IDProperty(self->group->id, cur, self->group->prop);
622         } else {
623                 return EXPP_ReturnPyObjError( PyExc_StopIteration,
624                                 "iterator at end" );
625         }
626 }
627
628 PyTypeObject IDGroup_Iter_Type = {
629         PyObject_HEAD_INIT( NULL )  /* required py macro */
630         0,                          /* ob_size */
631         /*  For printing, in format "<module>.<name>" */
632         "Blender IDGroup_Iter",           /* char *tp_name; */
633         sizeof( BPy_IDGroup_Iter ),       /* int tp_basicsize; */
634         0,                          /* tp_itemsize;  For allocation */
635
636         /* Methods to implement standard operations */
637
638         ( destructor ) IDGroup_Iter_dealloc,/* destructor tp_dealloc; */
639         NULL,                       /* printfunc tp_print; */
640         (getattrfunc) BPy_IDGroup_Iter_getattr,     /* getattrfunc tp_getattr; */
641         (setattrfunc) BPy_IDGroup_Iter_setattr,     /* setattrfunc tp_setattr; */
642         NULL,                       /* cmpfunc tp_compare; */
643         ( reprfunc ) IDGroup_Iter_repr,     /* reprfunc tp_repr; */
644
645         /* Method suites for standard classes */
646
647         NULL,                       /* PyNumberMethods *tp_as_number; */
648         NULL,                                   /* PySequenceMethods *tp_as_sequence; */
649         NULL,                       /* PyMappingMethods *tp_as_mapping; */
650
651         /* More standard operations (here for binary compatibility) */
652
653         NULL,                       /* hashfunc tp_hash; */
654         NULL,                       /* ternaryfunc tp_call; */
655         NULL,                       /* reprfunc tp_str; */
656         NULL,                       /* getattrofunc tp_getattro; */
657         NULL,                       /* setattrofunc tp_setattro; */
658
659         /* Functions to access object as input/output buffer */
660         NULL,                       /* PyBufferProcs *tp_as_buffer; */
661
662   /*** Flags to define presence of optional/expanded features ***/
663         Py_TPFLAGS_DEFAULT,         /* long tp_flags; */
664
665         NULL,                       /*  char *tp_doc;  Documentation string */
666   /*** Assigned meaning in release 2.0 ***/
667         /* call function for all accessible objects */
668         NULL,                       /* traverseproc tp_traverse; */
669
670         /* delete references to contained objects */
671         NULL,                       /* inquiry tp_clear; */
672
673   /***  Assigned meaning in release 2.1 ***/
674   /*** rich comparisons ***/
675         NULL,                       /* richcmpfunc tp_richcompare; */
676
677   /***  weak reference enabler ***/
678         0,                          /* long tp_weaklistoffset; */
679
680   /*** Added in release 2.2 ***/
681         /*   Iterators */
682         NULL,                       /* getiterfunc tp_iter; */
683         (iternextfunc) BPy_Group_Iter_Next, /* iternextfunc tp_iternext; */
684 };
685
686 /*********** ID Property Group wrapper **********/
687 PyObject *BPy_IDGroup_NewProperty(BPy_IDProperty *self, PyObject *args);
688 PyObject *BPy_IDGroup_DeleteProperty(BPy_IDProperty *self, PyObject *args);
689
690 static PyMethodDef BPy_IDGroup_methods[] = {
691         /* name, method, flags, doc */
692         {"newProperty", ( PyCFunction ) BPy_IDGroup_NewProperty, METH_VARARGS,
693          "Create a new ID property and attach to group."},
694          {"deleteProperty", ( PyCFunction ) BPy_IDGroup_DeleteProperty, METH_VARARGS,
695           "Delete an ID property.  Takes either a string of the id property to be deleted,\n \
696 or a reference to the ID property itself as an argument"},
697         {NULL},
698 };
699
700 void IDGroup_dealloc(void *self)
701 {
702         PyObject_DEL(self);
703 }
704
705 PyObject *IDGroup_repr(BPy_IDProperty *self)
706 {
707         return Py_BuildValue("s", "(ID Property Group)");
708 }
709
710 PyObject *BPy_IDGroup_getattr(BPy_IDProperty *self, char *name)
711 {
712         if (BSTR_EQ(name, "__members__")) return Py_BuildValue("[]");
713         else return Py_FindMethod( BPy_IDGroup_methods, ( PyObject * ) self, name );;
714 }
715
716 PyObject *BPy_IDGroup_setattr(BPy_IDProperty *self, PyObject *val, char *name)
717 {
718         return 0;
719 }
720
721 PyObject *BPy_IDGroup_SpawnIterator(BPy_IDProperty *self)
722 {
723         BPy_IDGroup_Iter *iter = PyObject_New(BPy_IDGroup_Iter, &IDGroup_Iter_Type);
724         iter->group = self;
725         iter->cur = self->prop->data.group.first;
726         Py_XINCREF(iter);
727         return (PyObject*) iter;
728 }
729
730 #define Default_Return "expected a string or int, a string plus variable number of additional arguments"
731 PyObject *BPy_IDGroup_NewProperty(BPy_IDProperty *self, PyObject *args)
732 {
733         IDProperty *prop = NULL;
734         IDPropertyTemplate val = {0};
735         PyObject *pyob, *pyprop=NULL;
736         char *name;
737         int nargs;
738         long type=0;
739
740         /*the arguments required differs depending on the ID type.  so we read
741           the first argument first before doing anything else. */
742         nargs = PySequence_Size(args);
743         if (nargs < 2) {
744                 return EXPP_ReturnPyObjError( PyExc_TypeError,
745                                 Default_Return);
746         }
747         pyob = PyTuple_GET_ITEM(args, 0);
748
749         /*we got a string instead of a type number as argument.*/
750         if (PyString_Check(pyob)) {
751                 char *st = PyString_AsString(pyob);
752                 if (BSTR_EQ(st, "String")) type = IDP_STRING;
753                 else if (BSTR_EQ(st, "Group")) type = IDP_GROUP;
754                 else if (BSTR_EQ(st, "Int")) type = IDP_INT;
755                 else if (BSTR_EQ(st, "Float")) type = IDP_FLOAT;
756                 else if (BSTR_EQ(st, "Array")) type = IDP_ARRAY;
757                 else return EXPP_ReturnPyObjError( PyExc_TypeError, "invalid id property type!");
758         } else {
759                 if (!PyNumber_Check(pyob)) {
760                         return EXPP_ReturnPyObjError( PyExc_TypeError,  Default_Return);
761                 }
762                 pyob = PyNumber_Int(pyob);
763                 if (pyob == NULL) {
764                         return EXPP_ReturnPyObjError( PyExc_TypeError,  Default_Return);
765                 }
766                 type = PyInt_AsLong(pyob);
767                 Py_XDECREF(pyob);
768         }
769
770         pyob = PyTuple_GET_ITEM(args, 1);
771         if (!PyString_Check(pyob))
772                 return EXPP_ReturnPyObjError( PyExc_TypeError,  Default_Return);
773
774         name = PyString_AsString(pyob);
775         //printf("name: %p %s\n", name, name);
776         //printf("group name: %s\n", self->prop->name);
777         switch (type) {
778                 case IDP_STRING:
779                 {
780                         if (nargs > 3) {
781                                 return EXPP_ReturnPyObjError( PyExc_TypeError,
782                                         "expected a string or an int, a string and optionally a string");
783                         }
784                         if (nargs == 3) {
785                                 val.str = PyString_AsString(PyTuple_GET_ITEM(args, 2));
786                         } else val.str = NULL;
787
788                         prop = IDP_New(IDP_STRING, val, name);
789                         break;
790                 }
791                 case IDP_GROUP:
792                         if (nargs != 2) {
793                                 return EXPP_ReturnPyObjError( PyExc_TypeError,
794                                         "expected a string or an int, and a string");
795                         }
796                         prop = IDP_New(IDP_GROUP, val, name);
797                         break;
798                 case IDP_INT:
799                         if (nargs != 2 && nargs != 3)
800                                 return EXPP_ReturnPyObjError( PyExc_TypeError,
801                                         "expected a string or an int, a string and optionally an int");
802                         val.i = 0;
803                         if (nargs == 3) {
804                                 pyob = PyTuple_GET_ITEM(args, 2);
805                                 if (!PyNumber_Check(pyob))
806                                         return EXPP_ReturnPyObjError( PyExc_TypeError,
807                                         "expected a string or an int, a string and optionally an int");
808                                 pyob = PyNumber_Int(pyob);
809                                 if (pyob == NULL)
810                                         return EXPP_ReturnPyObjError( PyExc_TypeError,
811                                         "expected a string or an int, a string and optionally an int");
812                                 val.i = (int) PyInt_AsLong(pyob);
813                                 Py_XDECREF(pyob);
814                         }
815                         prop = IDP_New(IDP_INT, val, name);
816                         break;
817                 case IDP_FLOAT:
818                         if (nargs != 2 && nargs != 3)
819                                 return EXPP_ReturnPyObjError( PyExc_TypeError,
820                                         "expected a string or an int, a string and optionally an int");
821                         val.i = 0;
822                         if (nargs == 3) {
823                                 pyob = PyTuple_GET_ITEM(args, 2);
824                                 if (!PyNumber_Check(pyob))
825                                         return EXPP_ReturnPyObjError( PyExc_TypeError,
826                                         "expected a string or an int, a string and optionally an int");
827                                 pyob = PyNumber_Float(pyob);
828                                 if (pyob == NULL)
829                                         return EXPP_ReturnPyObjError( PyExc_TypeError,
830                                         "expected a string or an int, a string and optionally an int");
831                                 val.f = (int) PyFloat_AS_DOUBLE(pyob);
832                                 Py_XDECREF(pyob);
833                         }
834                         prop = IDP_New(IDP_FLOAT, val, name);
835                         break;
836                 case IDP_ARRAY:
837                 {
838                         int arrtype=0;
839                         if (nargs != 4 && !PyNumber_Check(PyTuple_GET_ITEM(args, 2))
840                              && (!PyNumber_Check(PyTuple_GET_ITEM(args, 3)) || !PyString_Check(PyTuple_GET_ITEM(args, 3))))
841                                 return EXPP_ReturnPyObjError( PyExc_TypeError,
842                                         "expected a string or an int, a string, an int or a string, and an int");
843
844                         pyob = PyTuple_GET_ITEM(args, 2);
845                         if (PyNumber_Check(pyob)) {
846                                 pyob = PyNumber_Int(pyob);
847                                 if (pyob == NULL)
848                                         return EXPP_ReturnPyObjError( PyExc_TypeError,
849                                         "expected a string or an int, a string, an int or a string, and an int");
850
851                                 arrtype = (int)PyInt_AsLong(pyob);
852                                 Py_XDECREF(pyob);
853                                 if (arrtype != IDP_FLOAT && arrtype != IDP_INT)
854                                         EXPP_ReturnPyObjError( PyExc_TypeError, "invalid array type constant");
855                         } else {
856                                 char *st = PyString_AsString(pyob);
857                                 if (BSTR_EQ(st, "Float")) arrtype = IDP_FLOAT;
858                                 else if (BSTR_EQ(st, "Int")) arrtype = IDP_INT;
859                                 else return EXPP_ReturnPyObjError( PyExc_TypeError, "invalid array type");
860                         }
861
862                         if (arrtype == 0) return NULL;
863
864                         val.array.type = arrtype;
865                         pyob = PyNumber_Int(PyTuple_GET_ITEM(args, 3));
866                         if (pyob == NULL)
867                                 return EXPP_ReturnPyObjError( PyExc_TypeError,
868                                 "expected a string or an int, a string, an int or a string, and an int");
869
870                         val.array.len = (int)PyInt_AsLong(pyob);
871
872                         if (val.array.len <= 0)
873                                 return EXPP_ReturnPyObjError( PyExc_TypeError,
874                                 "array len must be greater then zero!");
875
876                         prop = IDP_New(IDP_ARRAY, val, name);
877                         if (!prop) return EXPP_ReturnPyObjError( PyExc_RuntimeError,
878                                 "error creating array!");
879                         break;
880                 }
881                 default:
882                         return EXPP_ReturnPyObjError( PyExc_TypeError,
883                                 "invalid id property type");
884         }
885
886         if (!IDP_AddToGroup(self->prop, prop)) {
887                 IDP_FreeProperty(prop);
888                 MEM_freeN(prop);
889                 return EXPP_ReturnPyObjError( PyExc_RuntimeError,
890                                 "property name already exists in group");
891         }
892         pyprop = BPy_Wrap_IDProperty(self->id, prop, self->prop);
893         //Py_XINCREF(pyprop);
894         return pyprop;
895 }
896
897 #undef Default_Return
898
899 PyObject *BPy_IDGroup_DeleteProperty(BPy_IDProperty *self, PyObject *args)
900 {
901         IDProperty *loop;
902         PyObject *ob;
903         char *name;
904         int nargs;
905
906         nargs = PySequence_Size(args);
907         if (nargs != 1)
908                 return EXPP_ReturnPyObjError( PyExc_TypeError,
909                                 "expected a string or IDProperty object");
910
911         ob = PyTuple_GET_ITEM(args, 0);
912         if (PyString_Check(ob)) {
913                 name = PyString_AsString(ob);
914                 for (loop=self->prop->data.group.first; loop; loop=loop->next) {
915                         if (BSTR_EQ(name, loop->name)) {
916                                 IDP_RemFromGroup(self->prop, loop);
917                                 IDP_FreeProperty(loop);
918                                 MEM_freeN(loop);
919                                 Py_INCREF( Py_None );
920                                 return Py_None;
921                         }
922                 }
923                 return EXPP_ReturnPyObjError( PyExc_AttributeError,
924                                 "IDProperty not in group!");
925
926         } else if (PyObject_TypeCheck(ob, &IDProperty_Type)) {
927                 for (loop=self->prop->data.group.first; loop; loop=loop->next) {
928                         if (loop == ((BPy_IDProperty*)ob)->prop) {
929                                 IDP_RemFromGroup(self->prop, loop);
930                                 IDP_FreeProperty(loop);
931                                 MEM_freeN(loop);
932                                 Py_INCREF( Py_None );
933                                 return Py_None;
934                         }
935                 }
936                 return EXPP_ReturnPyObjError( PyExc_AttributeError,
937                                 "IDProperty not in group!");
938         }
939
940         return EXPP_ReturnPyObjError( PyExc_AttributeError,
941                                 "expected a string or IDProperty object");
942 }
943
944 int BPy_IDGroup_Len(BPy_IDArray *self)
945 {
946         return self->prop->len;
947 }
948
949 PyObject *BPy_IDGroup_GetItem(BPy_IDProperty *self, int index)
950 {
951         IDProperty *prop;
952         int i;
953
954         for (prop=self->prop->data.group.first, i=0; prop; prop=prop->next, i++) {
955                 if (i == index) return BPy_Wrap_IDProperty(self->id, prop, self->prop);
956         }
957
958         return EXPP_ReturnPyObjError( PyExc_IndexError,
959                                         "index out of range!");
960 }
961
962 static PySequenceMethods BPy_IDGroup_Seq = {
963         (inquiry) BPy_IDGroup_Len,                      /* inquiry sq_length */
964         0,                                                                      /* binaryfunc sq_concat */
965         0,                                                                      /* intargfunc sq_repeat */
966         (intargfunc)BPy_IDGroup_GetItem,        /* intargfunc sq_item */
967         0,                                                                      /* intintargfunc sq_slice */
968         0,                                                                      /* intobjargproc sq_ass_item */
969         0,                                                                      /* intintobjargproc sq_ass_slice */
970         0,                                                                      /* objobjproc sq_contains */
971                                 /* Added in release 2.0 */
972         0,                                                                      /* binaryfunc sq_inplace_concat */
973         0,                                                                      /* intargfunc sq_inplace_repeat */
974 };
975
976 PyTypeObject IDGroup_Type = {
977         PyObject_HEAD_INIT( NULL )  /* required py macro */
978         0,                          /* ob_size */
979         /*  For printing, in format "<module>.<name>" */
980         "Blender IDGroup",           /* char *tp_name; */
981         sizeof( BPy_IDProperty ),       /* int tp_basicsize; */
982         0,                          /* tp_itemsize;  For allocation */
983
984         /* Methods to implement standard operations */
985
986         ( destructor ) IDGroup_dealloc,/* destructor tp_dealloc; */
987         NULL,                       /* printfunc tp_print; */
988         ( getattrfunc ) BPy_IDGroup_getattr, /* getattrfunc tp_getattr; */
989         ( setattrfunc ) BPy_IDGroup_setattr, /* setattrfunc tp_setattr; */
990         NULL,                       /* cmpfunc tp_compare; */
991         ( reprfunc ) IDGroup_repr,     /* reprfunc tp_repr; */
992
993         /* Method suites for standard classes */
994
995         NULL,                       /* PyNumberMethods *tp_as_number; */
996         &BPy_IDGroup_Seq,                       /* PySequenceMethods *tp_as_sequence; */
997         &BPy_IDProperty_Mapping,      /* PyMappingMethods *tp_as_mapping; */
998
999         /* More standard operations (here for binary compatibility) */
1000
1001         NULL,                       /* hashfunc tp_hash; */
1002         NULL,                       /* ternaryfunc tp_call; */
1003         NULL,                       /* reprfunc tp_str; */
1004         NULL,                       /* getattrofunc tp_getattro; */
1005         NULL,                       /* setattrofunc tp_setattro; */
1006
1007         /* Functions to access object as input/output buffer */
1008         NULL,                       /* PyBufferProcs *tp_as_buffer; */
1009
1010   /*** Flags to define presence of optional/expanded features ***/
1011         Py_TPFLAGS_DEFAULT,         /* long tp_flags; */
1012
1013         NULL,                       /*  char *tp_doc;  Documentation string */
1014   /*** Assigned meaning in release 2.0 ***/
1015         /* call function for all accessible objects */
1016         NULL,                       /* traverseproc tp_traverse; */
1017
1018         /* delete references to contained objects */
1019         NULL,                       /* inquiry tp_clear; */
1020
1021   /***  Assigned meaning in release 2.1 ***/
1022   /*** rich comparisons ***/
1023         NULL,                       /* richcmpfunc tp_richcompare; */
1024
1025   /***  weak reference enabler ***/
1026         0,                          /* long tp_weaklistoffset; */
1027
1028   /*** Added in release 2.2 ***/
1029         /*   Iterators */
1030         ( getiterfunc ) BPy_IDGroup_SpawnIterator,  /* getiterfunc tp_iter; */
1031         NULL,                                           /* iternextfunc tp_iternext; */
1032
1033   /*** Attribute descriptor and subclassing stuff ***/
1034         BPy_IDGroup_methods,        /* struct PyMethodDef *tp_methods; */
1035         NULL,                       /* struct PyMemberDef *tp_members; */
1036         NULL,       /* struct PyGetSetDef *tp_getset; */
1037         NULL,                       /* struct _typeobject *tp_base; */
1038         NULL,                       /* PyObject *tp_dict; */
1039         NULL,                       /* descrgetfunc tp_descr_get; */
1040         NULL,                       /* descrsetfunc tp_descr_set; */
1041         0,                          /* long tp_dictoffset; */
1042         NULL,                       /* initproc tp_init; */
1043         NULL,                       /* allocfunc tp_alloc; */
1044         NULL,                       /* newfunc tp_new; */
1045         /*  Low-level free-memory routine */
1046         NULL,                       /* freefunc tp_free;  */
1047 };
1048
1049 void IDProp_Init_Types(void)
1050 {
1051         PyType_Ready( &IDProperty_Type );
1052         PyType_Ready( &IDGroup_Type );
1053         PyType_Ready( &IDGroup_Iter_Type );
1054         PyType_Ready( &IDArray_Type );
1055 }