=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                         if (val.array.type == IDP_INT) {
253                                 item = PyNumber_Int(item);
254                                 ((int*)prop->data.pointer)[i] = (int)PyInt_AsLong(item);
255                         } else {
256                                 item = PyNumber_Float(item);
257                                 ((float*)prop->data.pointer)[i] = (float)PyFloat_AsDouble(item);
258                         }
259                         Py_XDECREF(item);
260                 }
261         } else if (PyMapping_Check(ob)) {
262                 PyObject *keys, *vals, *key, *pval;
263                 int i, len;
264                 /*yay! we get into recursive stuff now!*/
265                 keys = PyMapping_Keys(ob);
266                 vals = PyMapping_Values(ob);
267                 
268                 /*we allocate the group first; if we hit any invalid data,
269                   we can delete it easily enough.*/
270                 prop = IDP_New(IDP_GROUP, val, name);
271                 len = PyMapping_Length(ob);
272                 for (i=0; i<len; i++) {
273                         key = PySequence_GetItem(keys, i);
274                         pval = PySequence_GetItem(vals, i);
275                         if (!PyString_Check(key)) {
276                                 IDP_FreeProperty(prop);
277                                 MEM_freeN(prop);
278                                 Py_XDECREF(key);
279                                 Py_XDECREF(pval);
280                                 return "invalid element in subgroup dict template!";
281                         }
282                         if (BPy_IDProperty_Map_ValidateAndCreate(PyString_AsString(key), prop, pval)) {
283                                 IDP_FreeProperty(prop);
284                                 MEM_freeN(prop);
285                                 Py_XDECREF(key);
286                                 Py_XDECREF(pval);
287                                 return "invalid element in subgroup dict template!";
288                         }
289                         Py_XDECREF(key);
290                         Py_XDECREF(pval);
291                 }
292                 Py_XDECREF(keys);
293                 Py_XDECREF(vals);
294         } else return "invalid property value";
295         
296         if (IDP_GetPropertyFromGroup(group, prop->name) != NULL) {
297                 IDProperty *prop2 = IDP_GetPropertyFromGroup(group, prop->name);
298                 IDP_RemFromGroup(group, prop2);
299                 IDP_FreeProperty(prop2);
300                 MEM_freeN(prop2);
301         }
302
303         IDP_AddToGroup(group, prop);
304         return NULL;
305 }
306
307 int BPy_IDProperty_Map_SetItem(BPy_IDProperty *self, PyObject *key, PyObject *val)
308 {
309         char *err;
310         
311         if (self->prop->type  != IDP_GROUP)
312                 return EXPP_ReturnIntError( PyExc_TypeError,
313                         "unsubscriptable object");
314                         
315         if (!PyString_Check(key))
316                 return EXPP_ReturnIntError( PyExc_TypeError,
317                    "only strings are allowed as subgroup keys" );
318
319         if (val == NULL) {
320                 IDProperty *pkey = IDP_GetPropertyFromGroup(self->prop, PyString_AsString(key));
321                 if (pkey) {
322                         IDP_RemFromGroup(self->prop, pkey);
323                         IDP_FreeProperty(pkey);
324                         MEM_freeN(pkey);
325                         return 0;
326                 } else return EXPP_ReturnIntError( PyExc_RuntimeError, "property not found in group" );
327         }
328         
329         err = BPy_IDProperty_Map_ValidateAndCreate(PyString_AsString(key), self->prop, val);
330         if (err) return EXPP_ReturnIntError( PyExc_RuntimeError, err );
331         
332         return 0;
333 }
334
335 PyMappingMethods BPy_IDProperty_Mapping = {
336         (inquiry)BPy_IDProperty_Map_Len,                        /*inquiry mp_length */
337         (binaryfunc)BPy_IDProperty_Map_GetItem,         /*binaryfunc mp_subscript */
338         (objobjargproc)BPy_IDProperty_Map_SetItem,      /*objobjargproc mp_ass_subscript */
339 };
340
341 PyTypeObject IDProperty_Type = {
342         PyObject_HEAD_INIT( NULL )  /* required py macro */
343         0,                          /* ob_size */
344         /*  For printing, in format "<module>.<name>" */
345         "Blender IDProperty",           /* char *tp_name; */
346         sizeof( BPy_IDProperty ),       /* int tp_basicsize; */
347         0,                          /* tp_itemsize;  For allocation */
348
349         /* Methods to implement standard operations */
350
351         ( destructor ) IDProperty_dealloc,/* destructor tp_dealloc; */
352         NULL,                       /* printfunc tp_print; */
353         (getattrfunc) BPy_IDProperty_getattr,     /* getattrfunc tp_getattr; */
354         (setattrfunc) BPy_IDProperty_setattr,     /* setattrfunc tp_setattr; */
355         NULL,                       /* cmpfunc tp_compare; */
356         ( reprfunc ) IDProperty_repr,     /* reprfunc tp_repr; */
357
358         /* Method suites for standard classes */
359
360         NULL,                       /* PyNumberMethods *tp_as_number; */
361         NULL,                                   /* PySequenceMethods *tp_as_sequence; */
362         &BPy_IDProperty_Mapping,     /* PyMappingMethods *tp_as_mapping; */
363
364         /* More standard operations (here for binary compatibility) */
365
366         NULL,                       /* hashfunc tp_hash; */
367         NULL,                       /* ternaryfunc tp_call; */
368         NULL,                       /* reprfunc tp_str; */
369         NULL,                       /* getattrofunc tp_getattro; */
370         NULL,                       /* setattrofunc tp_setattro; */
371
372         /* Functions to access object as input/output buffer */
373         NULL,                       /* PyBufferProcs *tp_as_buffer; */
374
375   /*** Flags to define presence of optional/expanded features ***/
376         Py_TPFLAGS_DEFAULT,         /* long tp_flags; */
377
378         NULL,                       /*  char *tp_doc;  Documentation string */
379   /*** Assigned meaning in release 2.0 ***/
380         /* call function for all accessible objects */
381         NULL,                       /* traverseproc tp_traverse; */
382
383         /* delete references to contained objects */
384         NULL,                       /* inquiry tp_clear; */
385
386   /***  Assigned meaning in release 2.1 ***/
387   /*** rich comparisons ***/
388         NULL,                       /* richcmpfunc tp_richcompare; */
389
390   /***  weak reference enabler ***/
391         0,                          /* long tp_weaklistoffset; */
392
393   /*** Added in release 2.2 ***/
394         /*   Iterators */
395         NULL,                       /* getiterfunc tp_iter; */
396         NULL,                       /* iternextfunc tp_iternext; */
397 };
398
399 /*********** Main wrapping function *******/
400 PyObject *BPy_Wrap_IDProperty(ID *id, IDProperty *prop, IDProperty *parent)
401 {
402         BPy_IDProperty *wrap = PyObject_New(BPy_IDProperty, &IDProperty_Type);
403         wrap->prop = prop;
404         wrap->parent = parent;
405         wrap->id = id;
406         wrap->data_wrap = NULL;
407         //wrap->destroy = 0;
408         return (PyObject*) wrap;
409 }
410
411
412 /********Array Wrapper********/
413
414 void IDArray_dealloc(void *self)
415 {
416         PyObject_DEL(self);
417 }
418
419 PyObject *IDArray_repr(BPy_IDArray *self)
420 {
421         return Py_BuildValue("s", "(ID Array)");
422 }
423
424 PyObject *BPy_IDArray_getattr(BPy_IDArray *self, char *name)
425 {
426         if (BSTR_EQ(name, "len")) return Py_BuildValue("i", self->prop->len);
427         else if (BSTR_EQ(name, "type")) return Py_BuildValue("i", self->prop->subtype);
428         else return NULL;
429 }
430
431 int BPy_IDArray_setattr(BPy_IDArray *self, PyObject *val, char *name)
432 {
433         return -1;
434 }
435
436 int BPy_IDArray_Len(BPy_IDArray *self)
437 {
438         return self->prop->len;
439 }
440
441 PyObject *BPy_IDArray_GetItem(BPy_IDArray *self, int index)
442 {
443         if (index < 0 || index >= self->prop->len)
444                 return EXPP_ReturnPyObjError( PyExc_IndexError,
445                                 "index out of range!");
446
447         switch (self->prop->subtype) {
448                 case IDP_FLOAT:
449                         return Py_BuildValue("f", ((float*)self->prop->data.pointer)[index]);
450                         break;
451                 case IDP_INT:
452                         return Py_BuildValue("i", ((int*)self->prop->data.pointer)[index]);
453                         break;
454                 /*case IDP_LISTARRAY:
455                         return BPy_Wrap_IDProperty(self->id, ((IDProperty**)self->prop->data.array)[index]);
456                         break;*/
457         }
458                 return EXPP_ReturnPyObjError( PyExc_RuntimeError,
459                                 "invalid/corrupt array type!");
460 }
461
462 int BPy_IDArray_SetItem(BPy_IDArray *self, int index, PyObject *val)
463 {
464         int i;
465         float f;
466
467         if (index < 0 || index >= self->prop->len)
468                 return EXPP_ReturnIntError( PyExc_RuntimeError,
469                                 "index out of range!");
470
471         switch (self->prop->subtype) {
472                 case IDP_FLOAT:
473                         if (!PyNumber_Check(val)) return EXPP_ReturnIntError( PyExc_TypeError,
474                                 "expected a float");
475                         val = PyNumber_Float(val);
476                         if (!val) return EXPP_ReturnIntError( PyExc_TypeError,
477                                 "expected a float");
478
479                         f = (float) PyFloat_AsDouble(val);
480                         ((float*)self->prop->data.pointer)[index] = f;
481                         Py_XDECREF(val);
482                         break;
483                 case IDP_INT:
484                         if (!PyNumber_Check(val)) return EXPP_ReturnIntError( PyExc_TypeError,
485                                 "expected an int");
486                         val = PyNumber_Int(val);
487                         if (!val) return EXPP_ReturnIntError( PyExc_TypeError,
488                                 "expected an int");
489
490                         i = (int) PyInt_AsLong(val);
491                         ((int*)self->prop->data.pointer)[index] = i;
492                         Py_XDECREF(val);
493                         break;
494                 /*case IDP_LISTARRAY:
495                         if (!PyObject_TypeCheck(val, &IDProperty_Type))
496                                 return EXPP_ReturnIntError( PyExc_TypeError,
497                                 "expected an IDProperty");
498
499                         if (((IDProperty**)self->prop->data.array)[index]) {
500                                 IDP_FreeProperty(((IDProperty**)self->prop->data.array)[index]);
501                                 MEM_freeN(((IDProperty**)self->prop->data.array)[index]);
502                         }
503                         ((IDProperty**)self->prop->data.array)[index] = ((BPy_IDProperty*);
504                         break;*/
505         }
506         return 0;
507 }
508
509 static PySequenceMethods BPy_IDArray_Seq = {
510         (inquiry) BPy_IDArray_Len,                      /* inquiry sq_length */
511         0,                                                                      /* binaryfunc sq_concat */
512         0,                                                                      /* intargfunc sq_repeat */
513         (intargfunc)BPy_IDArray_GetItem,        /* intargfunc sq_item */
514         0,                                                                      /* intintargfunc sq_slice */
515         (intobjargproc)BPy_IDArray_SetItem,     /* intobjargproc sq_ass_item */
516         0,                                                                      /* intintobjargproc sq_ass_slice */
517         0,                                                                      /* objobjproc sq_contains */
518                                 /* Added in release 2.0 */
519         0,                                                                      /* binaryfunc sq_inplace_concat */
520         0,                                                                      /* intargfunc sq_inplace_repeat */
521 };
522
523 PyTypeObject IDArray_Type = {
524         PyObject_HEAD_INIT( NULL )  /* required py macro */
525         0,                          /* ob_size */
526         /*  For printing, in format "<module>.<name>" */
527         "Blender IDArray",           /* char *tp_name; */
528         sizeof( BPy_IDArray ),       /* int tp_basicsize; */
529         0,                          /* tp_itemsize;  For allocation */
530
531         /* Methods to implement standard operations */
532
533         ( destructor ) IDArray_dealloc,/* destructor tp_dealloc; */
534         NULL,                       /* printfunc tp_print; */
535         ( getattrfunc ) BPy_IDArray_getattr,     /* getattrfunc tp_getattr; */
536         ( setattrfunc ) BPy_IDArray_setattr,     /* setattrfunc tp_setattr; */
537         NULL,                       /* cmpfunc tp_compare; */
538         ( reprfunc ) IDArray_repr,     /* reprfunc tp_repr; */
539
540         /* Method suites for standard classes */
541
542         NULL,                       /* PyNumberMethods *tp_as_number; */
543         &BPy_IDArray_Seq,                       /* PySequenceMethods *tp_as_sequence; */
544         NULL,                       /* PyMappingMethods *tp_as_mapping; */
545
546         /* More standard operations (here for binary compatibility) */
547
548         NULL,                       /* hashfunc tp_hash; */
549         NULL,                       /* ternaryfunc tp_call; */
550         NULL,                       /* reprfunc tp_str; */
551         NULL,                       /* getattrofunc tp_getattro; */
552         NULL,                       /* setattrofunc tp_setattro; */
553
554         /* Functions to access object as input/output buffer */
555         NULL,                       /* PyBufferProcs *tp_as_buffer; */
556
557   /*** Flags to define presence of optional/expanded features ***/
558         Py_TPFLAGS_DEFAULT,         /* long tp_flags; */
559
560         NULL,                       /*  char *tp_doc;  Documentation string */
561   /*** Assigned meaning in release 2.0 ***/
562         /* call function for all accessible objects */
563         NULL,                       /* traverseproc tp_traverse; */
564
565         /* delete references to contained objects */
566         NULL,                       /* inquiry tp_clear; */
567
568   /***  Assigned meaning in release 2.1 ***/
569   /*** rich comparisons ***/
570         NULL,                       /* richcmpfunc tp_richcompare; */
571
572   /***  weak reference enabler ***/
573         0,                          /* long tp_weaklistoffset; */
574
575   /*** Added in release 2.2 ***/
576         /*   Iterators */
577         NULL,                       /* getiterfunc tp_iter; */
578         NULL,                       /* iternextfunc tp_iternext; */
579
580   /*** Attribute descriptor and subclassing stuff ***/
581         NULL,                       /* struct PyMethodDef *tp_methods; */
582         NULL,                       /* struct PyMemberDef *tp_members; */
583         NULL,       /* struct PyGetSetDef *tp_getset; */
584         NULL,                       /* struct _typeobject *tp_base; */
585         NULL,                       /* PyObject *tp_dict; */
586         NULL,                       /* descrgetfunc tp_descr_get; */
587         NULL,                       /* descrsetfunc tp_descr_set; */
588         0,                          /* long tp_dictoffset; */
589         NULL,                       /* initproc tp_init; */
590         NULL,                       /* allocfunc tp_alloc; */
591         NULL,                       /* newfunc tp_new; */
592         /*  Low-level free-memory routine */
593         NULL,                       /* freefunc tp_free;  */
594         /* For PyObject_IS_GC */
595         NULL,                       /* inquiry tp_is_gc;  */
596         NULL,                       /* PyObject *tp_bases; */
597         /* method resolution order */
598         NULL,                       /* PyObject *tp_mro;  */
599         NULL,                       /* PyObject *tp_cache; */
600         NULL,                       /* PyObject *tp_subclasses; */
601         NULL,                       /* PyObject *tp_weaklist; */
602         NULL
603 };
604
605 /*********** ID Property Group iterator ********/
606 void IDGroup_Iter_dealloc(void *self)
607 {
608         PyObject_DEL(self);
609 }
610
611 PyObject *IDGroup_Iter_repr(BPy_IDGroup_Iter *self)
612 {
613         return Py_BuildValue("s", "(ID Property Group)");
614 }
615
616 PyObject *BPy_IDGroup_Iter_getattr(BPy_IDGroup_Iter *self, char *name)
617 {
618         return Py_BuildValue("(s)", "None!! Not implemented!!");
619 }
620
621 PyObject *BPy_IDGroup_Iter_setattr(BPy_IDGroup_Iter *self, PyObject *val, char *name)
622 {
623         return 0;
624 }
625
626 PyObject *BPy_Group_Iter_Next(BPy_IDGroup_Iter *self)
627 {
628         IDProperty *cur=NULL;
629
630         if (self->cur) {
631                 cur = self->cur;
632                 self->cur = self->cur->next;
633                 return BPy_Wrap_IDProperty(self->group->id, cur, self->group->prop);
634         } else {
635                 return EXPP_ReturnPyObjError( PyExc_StopIteration,
636                                 "iterator at end" );
637         }
638 }
639
640 PyTypeObject IDGroup_Iter_Type = {
641         PyObject_HEAD_INIT( NULL )  /* required py macro */
642         0,                          /* ob_size */
643         /*  For printing, in format "<module>.<name>" */
644         "Blender IDGroup_Iter",           /* char *tp_name; */
645         sizeof( BPy_IDGroup_Iter ),       /* int tp_basicsize; */
646         0,                          /* tp_itemsize;  For allocation */
647
648         /* Methods to implement standard operations */
649
650         ( destructor ) IDGroup_Iter_dealloc,/* destructor tp_dealloc; */
651         NULL,                       /* printfunc tp_print; */
652         (getattrfunc) BPy_IDGroup_Iter_getattr,     /* getattrfunc tp_getattr; */
653         (setattrfunc) BPy_IDGroup_Iter_setattr,     /* setattrfunc tp_setattr; */
654         NULL,                       /* cmpfunc tp_compare; */
655         ( reprfunc ) IDGroup_Iter_repr,     /* reprfunc tp_repr; */
656
657         /* Method suites for standard classes */
658
659         NULL,                       /* PyNumberMethods *tp_as_number; */
660         NULL,                                   /* PySequenceMethods *tp_as_sequence; */
661         NULL,                       /* PyMappingMethods *tp_as_mapping; */
662
663         /* More standard operations (here for binary compatibility) */
664
665         NULL,                       /* hashfunc tp_hash; */
666         NULL,                       /* ternaryfunc tp_call; */
667         NULL,                       /* reprfunc tp_str; */
668         NULL,                       /* getattrofunc tp_getattro; */
669         NULL,                       /* setattrofunc tp_setattro; */
670
671         /* Functions to access object as input/output buffer */
672         NULL,                       /* PyBufferProcs *tp_as_buffer; */
673
674   /*** Flags to define presence of optional/expanded features ***/
675         Py_TPFLAGS_DEFAULT,         /* long tp_flags; */
676
677         NULL,                       /*  char *tp_doc;  Documentation string */
678   /*** Assigned meaning in release 2.0 ***/
679         /* call function for all accessible objects */
680         NULL,                       /* traverseproc tp_traverse; */
681
682         /* delete references to contained objects */
683         NULL,                       /* inquiry tp_clear; */
684
685   /***  Assigned meaning in release 2.1 ***/
686   /*** rich comparisons ***/
687         NULL,                       /* richcmpfunc tp_richcompare; */
688
689   /***  weak reference enabler ***/
690         0,                          /* long tp_weaklistoffset; */
691
692   /*** Added in release 2.2 ***/
693         /*   Iterators */
694         NULL,                       /* getiterfunc tp_iter; */
695         (iternextfunc) BPy_Group_Iter_Next, /* iternextfunc tp_iternext; */
696 };
697
698 /*********** ID Property Group wrapper **********/
699 PyObject *BPy_IDGroup_NewProperty(BPy_IDProperty *self, PyObject *args);
700 PyObject *BPy_IDGroup_DeleteProperty(BPy_IDProperty *self, PyObject *args);
701
702 static PyMethodDef BPy_IDGroup_methods[] = {
703         /* name, method, flags, doc */
704         {"newProperty", ( PyCFunction ) BPy_IDGroup_NewProperty, METH_VARARGS,
705          "Create a new ID property and attach to group."},
706          {"deleteProperty", ( PyCFunction ) BPy_IDGroup_DeleteProperty, METH_VARARGS,
707           "Delete an ID property.  Takes either a string of the id property to be deleted,\n \
708 or a reference to the ID property itself as an argument"},
709         {NULL},
710 };
711
712 void IDGroup_dealloc(void *self)
713 {
714         PyObject_DEL(self);
715 }
716
717 PyObject *IDGroup_repr(BPy_IDProperty *self)
718 {
719         return Py_BuildValue("s", "(ID Property Group)");
720 }
721
722 PyObject *BPy_IDGroup_getattr(BPy_IDProperty *self, char *name)
723 {
724         if (BSTR_EQ(name, "__members__")) return Py_BuildValue("[]");
725         else return Py_FindMethod( BPy_IDGroup_methods, ( PyObject * ) self, name );;
726 }
727
728 PyObject *BPy_IDGroup_setattr(BPy_IDProperty *self, PyObject *val, char *name)
729 {
730         return 0;
731 }
732
733 PyObject *BPy_IDGroup_SpawnIterator(BPy_IDProperty *self)
734 {
735         BPy_IDGroup_Iter *iter = PyObject_New(BPy_IDGroup_Iter, &IDGroup_Iter_Type);
736         iter->group = self;
737         iter->cur = self->prop->data.group.first;
738         Py_XINCREF(iter);
739         return (PyObject*) iter;
740 }
741
742 #define Default_Return "expected a string or int, a string plus variable number of additional arguments"
743 PyObject *BPy_IDGroup_NewProperty(BPy_IDProperty *self, PyObject *args)
744 {
745         IDProperty *prop = NULL;
746         IDPropertyTemplate val = {0};
747         PyObject *pyob, *pyprop=NULL;
748         char *name;
749         int nargs;
750         long type=0;
751
752         /*the arguments required differs depending on the ID type.  so we read
753           the first argument first before doing anything else. */
754         nargs = PySequence_Size(args);
755         if (nargs < 2) {
756                 return EXPP_ReturnPyObjError( PyExc_TypeError,
757                                 Default_Return);
758         }
759         pyob = PyTuple_GET_ITEM(args, 0);
760
761         /*we got a string instead of a type number as argument.*/
762         if (PyString_Check(pyob)) {
763                 char *st = PyString_AsString(pyob);
764                 if (BSTR_EQ(st, "String")) type = IDP_STRING;
765                 else if (BSTR_EQ(st, "Group")) type = IDP_GROUP;
766                 else if (BSTR_EQ(st, "Int")) type = IDP_INT;
767                 else if (BSTR_EQ(st, "Float")) type = IDP_FLOAT;
768                 else if (BSTR_EQ(st, "Array")) type = IDP_ARRAY;
769                 else return EXPP_ReturnPyObjError( PyExc_TypeError, "invalid id property type!");
770         } else {
771                 if (!PyNumber_Check(pyob)) {
772                         return EXPP_ReturnPyObjError( PyExc_TypeError,  Default_Return);
773                 }
774                 pyob = PyNumber_Int(pyob);
775                 if (pyob == NULL) {
776                         return EXPP_ReturnPyObjError( PyExc_TypeError,  Default_Return);
777                 }
778                 type = PyInt_AsLong(pyob);
779                 Py_XDECREF(pyob);
780         }
781
782         pyob = PyTuple_GET_ITEM(args, 1);
783         if (!PyString_Check(pyob))
784                 return EXPP_ReturnPyObjError( PyExc_TypeError,  Default_Return);
785
786         name = PyString_AsString(pyob);
787         //printf("name: %p %s\n", name, name);
788         //printf("group name: %s\n", self->prop->name);
789         switch (type) {
790                 case IDP_STRING:
791                 {
792                         if (nargs > 3) {
793                                 return EXPP_ReturnPyObjError( PyExc_TypeError,
794                                         "expected a string or an int, a string and optionally a string");
795                         }
796                         if (nargs == 3) {
797                                 val.str = PyString_AsString(PyTuple_GET_ITEM(args, 2));
798                         } else val.str = NULL;
799
800                         prop = IDP_New(IDP_STRING, val, name);
801                         break;
802                 }
803                 case IDP_GROUP:
804                         if (nargs != 2) {
805                                 return EXPP_ReturnPyObjError( PyExc_TypeError,
806                                         "expected a string or an int, and a string");
807                         }
808                         prop = IDP_New(IDP_GROUP, val, name);
809                         break;
810                 case IDP_INT:
811                         if (nargs != 2 && nargs != 3)
812                                 return EXPP_ReturnPyObjError( PyExc_TypeError,
813                                         "expected a string or an int, a string and optionally an int");
814                         val.i = 0;
815                         if (nargs == 3) {
816                                 pyob = PyTuple_GET_ITEM(args, 2);
817                                 if (!PyNumber_Check(pyob))
818                                         return EXPP_ReturnPyObjError( PyExc_TypeError,
819                                         "expected a string or an int, a string and optionally an int");
820                                 pyob = PyNumber_Int(pyob);
821                                 if (pyob == NULL)
822                                         return EXPP_ReturnPyObjError( PyExc_TypeError,
823                                         "expected a string or an int, a string and optionally an int");
824                                 val.i = (int) PyInt_AsLong(pyob);
825                                 Py_XDECREF(pyob);
826                         }
827                         prop = IDP_New(IDP_INT, val, name);
828                         break;
829                 case IDP_FLOAT:
830                         if (nargs != 2 && nargs != 3)
831                                 return EXPP_ReturnPyObjError( PyExc_TypeError,
832                                         "expected a string or an int, a string and optionally an int");
833                         val.i = 0;
834                         if (nargs == 3) {
835                                 pyob = PyTuple_GET_ITEM(args, 2);
836                                 if (!PyNumber_Check(pyob))
837                                         return EXPP_ReturnPyObjError( PyExc_TypeError,
838                                         "expected a string or an int, a string and optionally an int");
839                                 pyob = PyNumber_Float(pyob);
840                                 if (pyob == NULL)
841                                         return EXPP_ReturnPyObjError( PyExc_TypeError,
842                                         "expected a string or an int, a string and optionally an int");
843                                 val.f = (int) PyFloat_AS_DOUBLE(pyob);
844                                 Py_XDECREF(pyob);
845                         }
846                         prop = IDP_New(IDP_FLOAT, val, name);
847                         break;
848                 case IDP_ARRAY:
849                 {
850                         int arrtype=0;
851                         if (nargs != 4 && !PyNumber_Check(PyTuple_GET_ITEM(args, 2))
852                              && (!PyNumber_Check(PyTuple_GET_ITEM(args, 3)) || !PyString_Check(PyTuple_GET_ITEM(args, 3))))
853                                 return EXPP_ReturnPyObjError( PyExc_TypeError,
854                                         "expected a string or an int, a string, an int or a string, and an int");
855
856                         pyob = PyTuple_GET_ITEM(args, 2);
857                         if (PyNumber_Check(pyob)) {
858                                 pyob = PyNumber_Int(pyob);
859                                 if (pyob == NULL)
860                                         return EXPP_ReturnPyObjError( PyExc_TypeError,
861                                         "expected a string or an int, a string, an int or a string, and an int");
862
863                                 arrtype = (int)PyInt_AsLong(pyob);
864                                 Py_XDECREF(pyob);
865                                 if (arrtype != IDP_FLOAT && arrtype != IDP_INT)
866                                         EXPP_ReturnPyObjError( PyExc_TypeError, "invalid array type constant");
867                         } else {
868                                 char *st = PyString_AsString(pyob);
869                                 if (BSTR_EQ(st, "Float")) arrtype = IDP_FLOAT;
870                                 else if (BSTR_EQ(st, "Int")) arrtype = IDP_INT;
871                                 else return EXPP_ReturnPyObjError( PyExc_TypeError, "invalid array type");
872                         }
873
874                         if (arrtype == 0) return NULL;
875
876                         val.array.type = arrtype;
877                         pyob = PyNumber_Int(PyTuple_GET_ITEM(args, 3));
878                         if (pyob == NULL)
879                                 return EXPP_ReturnPyObjError( PyExc_TypeError,
880                                 "expected a string or an int, a string, an int or a string, and an int");
881
882                         val.array.len = (int)PyInt_AsLong(pyob);
883
884                         if (val.array.len <= 0)
885                                 return EXPP_ReturnPyObjError( PyExc_TypeError,
886                                 "array len must be greater then zero!");
887
888                         prop = IDP_New(IDP_ARRAY, val, name);
889                         if (!prop) return EXPP_ReturnPyObjError( PyExc_RuntimeError,
890                                 "error creating array!");
891                         break;
892                 }
893                 default:
894                         return EXPP_ReturnPyObjError( PyExc_TypeError,
895                                 "invalid id property type");
896         }
897
898         if (!IDP_AddToGroup(self->prop, prop)) {
899                 IDP_FreeProperty(prop);
900                 MEM_freeN(prop);
901                 return EXPP_ReturnPyObjError( PyExc_RuntimeError,
902                                 "property name already exists in group");
903         }
904         pyprop = BPy_Wrap_IDProperty(self->id, prop, self->prop);
905         //Py_XINCREF(pyprop);
906         return pyprop;
907 }
908
909 #undef Default_Return
910
911 PyObject *BPy_IDGroup_DeleteProperty(BPy_IDProperty *self, PyObject *args)
912 {
913         IDProperty *loop;
914         PyObject *ob;
915         char *name;
916         int nargs;
917
918         nargs = PySequence_Size(args);
919         if (nargs != 1)
920                 return EXPP_ReturnPyObjError( PyExc_TypeError,
921                                 "expected a string or IDProperty object");
922
923         ob = PyTuple_GET_ITEM(args, 0);
924         if (PyString_Check(ob)) {
925                 name = PyString_AsString(ob);
926                 for (loop=self->prop->data.group.first; loop; loop=loop->next) {
927                         if (BSTR_EQ(name, loop->name)) {
928                                 IDP_RemFromGroup(self->prop, loop);
929                                 IDP_FreeProperty(loop);
930                                 MEM_freeN(loop);
931                                 Py_INCREF( Py_None );
932                                 return Py_None;
933                         }
934                 }
935                 return EXPP_ReturnPyObjError( PyExc_AttributeError,
936                                 "IDProperty not in group!");
937
938         } else if (PyObject_TypeCheck(ob, &IDProperty_Type)) {
939                 for (loop=self->prop->data.group.first; loop; loop=loop->next) {
940                         if (loop == ((BPy_IDProperty*)ob)->prop) {
941                                 IDP_RemFromGroup(self->prop, loop);
942                                 IDP_FreeProperty(loop);
943                                 MEM_freeN(loop);
944                                 Py_INCREF( Py_None );
945                                 return Py_None;
946                         }
947                 }
948                 return EXPP_ReturnPyObjError( PyExc_AttributeError,
949                                 "IDProperty not in group!");
950         }
951
952         return EXPP_ReturnPyObjError( PyExc_AttributeError,
953                                 "expected a string or IDProperty object");
954 }
955
956 int BPy_IDGroup_Len(BPy_IDArray *self)
957 {
958         return self->prop->len;
959 }
960
961 PyObject *BPy_IDGroup_GetItem(BPy_IDProperty *self, int index)
962 {
963         IDProperty *prop;
964         int i;
965
966         for (prop=self->prop->data.group.first, i=0; prop; prop=prop->next, i++) {
967                 if (i == index) return BPy_Wrap_IDProperty(self->id, prop, self->prop);
968         }
969
970         return EXPP_ReturnPyObjError( PyExc_IndexError,
971                                         "index out of range!");
972 }
973
974 static PySequenceMethods BPy_IDGroup_Seq = {
975         (inquiry) BPy_IDGroup_Len,                      /* inquiry sq_length */
976         0,                                                                      /* binaryfunc sq_concat */
977         0,                                                                      /* intargfunc sq_repeat */
978         (intargfunc)BPy_IDGroup_GetItem,        /* intargfunc sq_item */
979         0,                                                                      /* intintargfunc sq_slice */
980         0,                                                                      /* intobjargproc sq_ass_item */
981         0,                                                                      /* intintobjargproc sq_ass_slice */
982         0,                                                                      /* objobjproc sq_contains */
983                                 /* Added in release 2.0 */
984         0,                                                                      /* binaryfunc sq_inplace_concat */
985         0,                                                                      /* intargfunc sq_inplace_repeat */
986 };
987
988 PyTypeObject IDGroup_Type = {
989         PyObject_HEAD_INIT( NULL )  /* required py macro */
990         0,                          /* ob_size */
991         /*  For printing, in format "<module>.<name>" */
992         "Blender IDGroup",           /* char *tp_name; */
993         sizeof( BPy_IDProperty ),       /* int tp_basicsize; */
994         0,                          /* tp_itemsize;  For allocation */
995
996         /* Methods to implement standard operations */
997
998         ( destructor ) IDGroup_dealloc,/* destructor tp_dealloc; */
999         NULL,                       /* printfunc tp_print; */
1000         ( getattrfunc ) BPy_IDGroup_getattr, /* getattrfunc tp_getattr; */
1001         ( setattrfunc ) BPy_IDGroup_setattr, /* setattrfunc tp_setattr; */
1002         NULL,                       /* cmpfunc tp_compare; */
1003         ( reprfunc ) IDGroup_repr,     /* reprfunc tp_repr; */
1004
1005         /* Method suites for standard classes */
1006
1007         NULL,                       /* PyNumberMethods *tp_as_number; */
1008         &BPy_IDGroup_Seq,                       /* PySequenceMethods *tp_as_sequence; */
1009         &BPy_IDProperty_Mapping,      /* PyMappingMethods *tp_as_mapping; */
1010
1011         /* More standard operations (here for binary compatibility) */
1012
1013         NULL,                       /* hashfunc tp_hash; */
1014         NULL,                       /* ternaryfunc tp_call; */
1015         NULL,                       /* reprfunc tp_str; */
1016         NULL,                       /* getattrofunc tp_getattro; */
1017         NULL,                       /* setattrofunc tp_setattro; */
1018
1019         /* Functions to access object as input/output buffer */
1020         NULL,                       /* PyBufferProcs *tp_as_buffer; */
1021
1022   /*** Flags to define presence of optional/expanded features ***/
1023         Py_TPFLAGS_DEFAULT,         /* long tp_flags; */
1024
1025         NULL,                       /*  char *tp_doc;  Documentation string */
1026   /*** Assigned meaning in release 2.0 ***/
1027         /* call function for all accessible objects */
1028         NULL,                       /* traverseproc tp_traverse; */
1029
1030         /* delete references to contained objects */
1031         NULL,                       /* inquiry tp_clear; */
1032
1033   /***  Assigned meaning in release 2.1 ***/
1034   /*** rich comparisons ***/
1035         NULL,                       /* richcmpfunc tp_richcompare; */
1036
1037   /***  weak reference enabler ***/
1038         0,                          /* long tp_weaklistoffset; */
1039
1040   /*** Added in release 2.2 ***/
1041         /*   Iterators */
1042         ( getiterfunc ) BPy_IDGroup_SpawnIterator,  /* getiterfunc tp_iter; */
1043         NULL,                                           /* iternextfunc tp_iternext; */
1044
1045   /*** Attribute descriptor and subclassing stuff ***/
1046         BPy_IDGroup_methods,        /* struct PyMethodDef *tp_methods; */
1047         NULL,                       /* struct PyMemberDef *tp_members; */
1048         NULL,       /* struct PyGetSetDef *tp_getset; */
1049         NULL,                       /* struct _typeobject *tp_base; */
1050         NULL,                       /* PyObject *tp_dict; */
1051         NULL,                       /* descrgetfunc tp_descr_get; */
1052         NULL,                       /* descrsetfunc tp_descr_set; */
1053         0,                          /* long tp_dictoffset; */
1054         NULL,                       /* initproc tp_init; */
1055         NULL,                       /* allocfunc tp_alloc; */
1056         NULL,                       /* newfunc tp_new; */
1057         /*  Low-level free-memory routine */
1058         NULL,                       /* freefunc tp_free;  */
1059 };
1060
1061 void IDProp_Init_Types(void)
1062 {
1063         PyType_Ready( &IDProperty_Type );
1064         PyType_Ready( &IDGroup_Type );
1065         PyType_Ready( &IDGroup_Iter_Type );
1066         PyType_Ready( &IDArray_Type );
1067 }