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