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