=IDProperties Python 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 extern PyTypeObject IDGroup_Iter_Type;
47
48 /*********************** ID Property Main Wrapper Stuff ***************/
49 void IDGroup_dealloc( BPy_IDProperty *self )
50 {
51         PyObject_DEL( self );
52 }
53
54 PyObject *IDGroup_repr( BPy_IDProperty *self )
55 {
56         return Py_BuildValue( "s", "(ID Property)" );
57 }
58
59 extern PyTypeObject IDGroup_Type;
60
61 PyObject *BPy_IDGroup_WrapData( ID *id, IDProperty *prop )
62 {
63         switch ( prop->type ) {
64                 case IDP_STRING:
65                         return Py_BuildValue( "s", prop->data.pointer );
66                 case IDP_INT:
67                         return Py_BuildValue( "i", prop->data.val );
68                 case IDP_FLOAT:
69                         return Py_BuildValue( "f", *(float*)(&prop->data.val) );
70                 case IDP_GROUP:
71                         /*blegh*/
72                         {
73                                 BPy_IDProperty *group = PyObject_New(BPy_IDProperty, &IDGroup_Type);
74                                 if (!group)
75                                         return EXPP_ReturnPyObjError( PyExc_RuntimeError,
76                                            "PyObject_New() failed" );
77                         
78                                 group->id = id;
79                                 group->prop = prop;
80                                 return (PyObject*) group;
81                         }
82                 case IDP_ARRAY:
83                         {
84                                 BPy_IDProperty *array = PyObject_New(BPy_IDProperty, &IDArray_Type);
85                                 if (!array)
86                                         return EXPP_ReturnPyObjError( PyExc_RuntimeError,
87                                            "PyObject_New() failed" );
88                                            
89                                 array->id = id;
90                                 array->prop = prop;
91                                 return (PyObject*) array;
92                         }
93                 case IDP_MATRIX:
94                 case IDP_VECTOR:
95                         break;
96         }
97         Py_RETURN_NONE;
98 }
99
100 int BPy_IDGroup_SetData(BPy_IDProperty *self, IDProperty *prop, PyObject *value)
101 {
102         switch (prop->type) {
103                 case IDP_STRING:
104                 {
105                         char *st;
106                         if (!PyString_Check(value))
107                                 return EXPP_ReturnIntError(PyExc_TypeError, "expected a string!");
108
109                         st = PyString_AsString(value);
110                         IDP_ResizeArray(prop, strlen(st)+1);
111                         strcpy(prop->data.pointer, st);
112                         return 0;
113                 }
114
115                 case IDP_INT:
116                 {
117                         int ivalue;
118                         if (!PyNumber_Check(value))
119                                 return EXPP_ReturnIntError(PyExc_TypeError, "expected an int!");
120                         value = PyNumber_Int(value);
121                         if (!value)
122                                 return EXPP_ReturnIntError(PyExc_TypeError, "expected an int!");
123                         ivalue = (int) PyInt_AsLong(value);
124                         prop->data.val = ivalue;
125                         Py_XDECREF(value);
126                         break;
127                 }
128                 case IDP_FLOAT:
129                 {
130                         float fvalue;
131                         if (!PyNumber_Check(value))
132                                 return EXPP_ReturnIntError(PyExc_TypeError, "expected a float!");
133                         value = PyNumber_Float(value);
134                         if (!value)
135                                 return EXPP_ReturnIntError(PyExc_TypeError, "expected a float!");
136                         fvalue = (float) PyFloat_AsDouble(value);
137                         *(float*)&self->prop->data.val = fvalue;
138                         Py_XDECREF(value);
139                         break;
140                 }
141
142                 default:
143                         return EXPP_ReturnIntError(PyExc_AttributeError, "attempt to set read-only attribute!");
144         }
145         return 0;
146 }
147
148 PyObject *BPy_IDGroup_GetName(BPy_IDProperty *self)
149 {
150         return Py_BuildValue("s", self->prop->name);
151 }
152
153 int BPy_IDGroup_SetName(BPy_IDProperty *self, PyObject *value)
154 {
155         char *st;
156         if (!PyString_Check(value))
157                 return EXPP_ReturnIntError(PyExc_TypeError, "expected a string!");
158
159         st = PyString_AsString(value);
160         if (strlen(st) >= MAX_IDPROP_NAME)
161                 return EXPP_ReturnIntError(PyExc_TypeError, "string length cannot exceed 31 characters!");
162
163         strcpy(self->prop->name, st);
164         return 0;
165 }
166
167 PyObject *BPy_IDGroup_GetType(BPy_IDProperty *self)
168 {
169         return Py_BuildValue("i", self->prop->type);
170 }
171
172 static PyGetSetDef BPy_IDGroup_getseters[] = {
173         {"name",
174          (getter)BPy_IDGroup_GetName, (setter)BPy_IDGroup_SetName,
175          "The name of this Group.",
176          NULL},
177          {NULL, NULL, NULL, NULL, NULL}
178 };
179          
180 int BPy_IDGroup_Map_Len(BPy_IDProperty *self)
181 {
182         if (self->prop->type != IDP_GROUP)
183                 return EXPP_ReturnIntError( PyExc_TypeError,
184                         "len() of unsized object");
185                         
186         return self->prop->len;
187 }
188
189 PyObject *BPy_IDGroup_Map_GetItem(BPy_IDProperty *self, PyObject *item)
190 {
191         IDProperty *loop;
192         char *st;
193         
194         if (self->prop->type  != IDP_GROUP)
195                 return EXPP_ReturnPyObjError( PyExc_TypeError,
196                         "unsubscriptable object");
197                         
198         if (!PyString_Check(item)) 
199                 return EXPP_ReturnPyObjError( PyExc_TypeError,
200                         "only strings are allowed as keys of ID properties");
201         
202         st = PyString_AsString(item);
203         for (loop=self->prop->data.group.first; loop; loop=loop->next) {
204                 if (BSTR_EQ(loop->name, st)) return BPy_IDGroup_WrapData(self->id, loop);
205         }
206         return EXPP_ReturnPyObjError( PyExc_KeyError,
207                 "key not in subgroup dict");
208 }
209
210 /*returns NULL on success, error string on failure*/
211 char *BPy_IDProperty_Map_ValidateAndCreate(char *name, IDProperty *group, PyObject *ob)
212 {
213         IDProperty *prop = NULL, *prop2=NULL, *prev=NULL;
214         IDPropertyTemplate val;
215         
216         if (PyFloat_Check(ob)) {
217                 val.f = (float) PyFloat_AsDouble(ob);
218                 prop = IDP_New(IDP_FLOAT, val, name);
219         } else if (PyInt_Check(ob)) {
220                 val.i = (int) PyInt_AsLong(ob);
221                 prop = IDP_New(IDP_INT, val, name);
222         } else if (PyString_Check(ob)) {
223                 val.str = PyString_AsString(ob);
224                 prop = IDP_New(IDP_STRING, val, name);
225         } else if (PySequence_Check(ob)) {
226                 PyObject *item;
227                 int i;
228                 
229                 /*validate sequence and derive type.
230                 we assume IDP_INT unless we hit a float
231                 number; then we assume it's */
232                 val.array.type = IDP_INT;
233                 val.array.len = PySequence_Length(ob);
234                 for (i=0; i<val.array.len; i++) {
235                         item = PySequence_GetItem(ob, i);
236                         if (PyFloat_Check(item)) val.array.type = IDP_FLOAT;
237                         else if (!PyInt_Check(item)) return "only floats and ints are allowed in ID property arrays";
238                         Py_XDECREF(item);
239                 }
240                 
241                 prop = IDP_New(IDP_ARRAY, val, name);
242                 for (i=0; i<val.array.len; i++) {
243                         item = PySequence_GetItem(ob, i);
244                         if (val.array.type == IDP_INT) {
245                                 item = PyNumber_Int(item);
246                                 ((int*)prop->data.pointer)[i] = (int)PyInt_AsLong(item);
247                         } else {
248                                 item = PyNumber_Float(item);
249                                 ((float*)prop->data.pointer)[i] = (float)PyFloat_AsDouble(item);
250                         }
251                         Py_XDECREF(item);
252                 }
253         } else if (PyMapping_Check(ob)) {
254                 PyObject *keys, *vals, *key, *pval;
255                 int i, len;
256                 /*yay! we get into recursive stuff now!*/
257                 keys = PyMapping_Keys(ob);
258                 vals = PyMapping_Values(ob);
259                 
260                 /*we allocate the group first; if we hit any invalid data,
261                   we can delete it easily enough.*/
262                 prop = IDP_New(IDP_GROUP, val, name);
263                 len = PyMapping_Length(ob);
264                 for (i=0; i<len; i++) {
265                         key = PySequence_GetItem(keys, i);
266                         pval = PySequence_GetItem(vals, i);
267                         if (!PyString_Check(key)) {
268                                 IDP_FreeProperty(prop);
269                                 MEM_freeN(prop);
270                                 Py_XDECREF(keys);
271                                 Py_XDECREF(vals);
272                                 Py_XDECREF(key);
273                                 Py_XDECREF(pval);
274                                 return "invalid element in subgroup dict template!";
275                         }
276                         if (BPy_IDProperty_Map_ValidateAndCreate(PyString_AsString(key), prop, pval)) {
277                                 IDP_FreeProperty(prop);
278                                 MEM_freeN(prop);
279                                 Py_XDECREF(keys);
280                                 Py_XDECREF(vals);
281                                 Py_XDECREF(key);
282                                 Py_XDECREF(pval);
283                                 return "invalid element in subgroup dict template!";
284                         }
285                         Py_XDECREF(key);
286                         Py_XDECREF(pval);
287                 }
288                 Py_XDECREF(keys);
289                 Py_XDECREF(vals);
290         } else return "invalid property value";
291         
292         prop2 = IDP_GetPropertyFromGroup(group, prop->name);
293         if (prop2) {
294                 prev=prop2->prev; /*we want to insert new property in same place as old*/
295                 IDP_RemFromGroup(group, prop2);
296                 IDP_FreeProperty(prop2);
297                 MEM_freeN(prop2);
298         }
299
300         IDP_InsertToGroup(group, prev, prop);
301         return NULL;
302 }
303
304 int BPy_IDGroup_Map_SetItem(BPy_IDProperty *self, PyObject *key, PyObject *val)
305 {
306         char *err;
307         
308         if (self->prop->type  != IDP_GROUP)
309                 return EXPP_ReturnIntError( PyExc_TypeError,
310                         "unsubscriptable object");
311                         
312         if (!PyString_Check(key))
313                 return EXPP_ReturnIntError( PyExc_TypeError,
314                    "only strings are allowed as subgroup keys" );
315
316         if (val == NULL) {
317                 IDProperty *pkey = IDP_GetPropertyFromGroup(self->prop, PyString_AsString(key));
318                 if (pkey) {
319                         IDP_RemFromGroup(self->prop, pkey);
320                         IDP_FreeProperty(pkey);
321                         MEM_freeN(pkey);
322                         return 0;
323                 } else return EXPP_ReturnIntError( PyExc_RuntimeError, "property not found in group" );
324         }
325         
326         err = BPy_IDProperty_Map_ValidateAndCreate(PyString_AsString(key), self->prop, val);
327         if (err) return EXPP_ReturnIntError( PyExc_RuntimeError, err );
328         
329         return 0;
330 }
331
332 PyObject *BPy_IDGroup_SpawnIterator(BPy_IDProperty *self)
333 {
334         BPy_IDGroup_Iter *iter = PyObject_New(BPy_IDGroup_Iter, &IDGroup_Iter_Type);
335         
336         if (!iter)
337                 return EXPP_ReturnPyObjError( PyExc_RuntimeError,
338                    "PyObject_New() failed" );
339         iter->group = self;
340         iter->mode = IDPROP_ITER_KEYS;
341         iter->cur = self->prop->data.group.first;
342         Py_XINCREF(iter);
343         return (PyObject*) iter;
344 }
345
346 PyObject *BPy_IDGroup_MapDataToPy(IDProperty *prop)
347 {
348         switch (prop->type) {
349                 case IDP_STRING:
350                         return Py_BuildValue("s", prop->data.pointer);
351                         break;
352                 case IDP_FLOAT:
353                         return Py_BuildValue("f", *((float*)&prop->data.val));
354                         break;
355                 case IDP_INT:
356                         return Py_BuildValue("i", prop->data.val);
357                         break;
358                 case IDP_ARRAY:
359                 {
360                         PyObject *seq = PyList_New(prop->len);
361                         int i;
362                         
363                         if (!seq) 
364                                 return EXPP_ReturnPyObjError( PyExc_RuntimeError,
365                                            "PyList_New() failed" );
366                         
367                         for (i=0; i<prop->len; i++) {
368                                 if (prop->subtype == IDP_FLOAT)
369                                         PyList_SetItem(seq, i, Py_BuildValue("f", ((float*)prop->data.pointer)[i]));
370                                 else PyList_SetItem(seq, i, Py_BuildValue("i",   ((int*)prop->data.pointer)[i]));
371                         }
372                         return seq;
373                 }
374                 case IDP_GROUP:
375                 {
376                         PyObject *dict = PyDict_New(), *wrap;
377                         IDProperty *loop;
378                         
379                         if (!dict)
380                                 return EXPP_ReturnPyObjError( PyExc_RuntimeError,
381                                            "PyDict_New() failed" );
382                                            
383                         for (loop=prop->data.group.first; loop; loop=loop->next) {
384                                 wrap = BPy_IDGroup_MapDataToPy(loop);
385                                 if (!wrap) 
386                                         return EXPP_ReturnPyObjError( PyExc_RuntimeError,
387                                            "BPy_IDGroup_MapDataToPy() failed" );
388                                            
389                                 PyDict_SetItemString(dict, loop->name, wrap);
390                         }
391                         return dict;
392                 }
393         }
394         
395         return EXPP_ReturnPyObjError( PyExc_RuntimeError,
396                                            "eek!! a property exists with a bad type code!!!" );
397 }
398
399 PyObject *BPy_IDGroup_Pop(BPy_IDProperty *self, PyObject *vars)
400 {
401         IDProperty *loop;
402         PyObject *pyform;
403         char *name;
404         int ok = PyArg_ParseTuple(vars, "s", &name);
405         
406         if (!ok) {
407                 return EXPP_ReturnPyObjError( PyExc_TypeError,
408                    "pop expected at least 1 arguments, got 0" );
409         }
410         
411         for (loop=self->prop->data.group.first; loop; loop=loop->next) {
412                 if (BSTR_EQ(loop->name, name)) {
413                         pyform = BPy_IDGroup_MapDataToPy(loop);
414                         
415                         if (!pyform)
416                                 /*ok something bad happened with the pyobject,
417                                   so don't remove the prop from the group.  if pyform is
418                                   NULL, then it already should have raised an exception.*/
419                                   return NULL;
420
421                         IDP_RemFromGroup(self->prop, loop);
422                         return pyform;
423                 }
424         }
425         
426         return EXPP_ReturnPyObjError( PyExc_KeyError,
427                    "item not in group" );
428 }
429
430 PyObject *BPy_IDGroup_IterItems(BPy_IDProperty *self)
431 {
432         BPy_IDGroup_Iter *iter = PyObject_New(BPy_IDGroup_Iter, &IDGroup_Iter_Type);
433         
434         if (!iter)
435                 return EXPP_ReturnPyObjError( PyExc_RuntimeError,
436                    "PyObject_New() failed" );
437         
438         iter->group = self;
439         iter->mode = IDPROP_ITER_ITEMS;
440         iter->cur = self->prop->data.group.first;
441         Py_XINCREF(iter);
442         return (PyObject*) iter;
443 }
444
445 PyObject *BPy_IDGroup_GetKeys(BPy_IDProperty *self)
446 {
447         PyObject *seq = PyList_New(self->prop->len), *st;
448         IDProperty *loop;
449         int i;
450
451         if (!seq) 
452                 return EXPP_ReturnPyObjError( PyExc_RuntimeError,
453                    "PyList_New() failed" );
454                    
455         for (i=0, loop=self->prop->data.group.first; loop; loop=loop->next, i++) {
456                 st = Py_BuildValue("s", loop->name);
457                 PyList_SetItem(seq, i, st);
458         }
459         
460         return seq;
461 }
462
463 PyObject *BPy_IDGroup_GetValues(BPy_IDProperty *self)
464 {
465         PyObject *seq = PyList_New(self->prop->len);
466         IDProperty *loop;
467         int i;
468
469         if (!seq) 
470                 return EXPP_ReturnPyObjError( PyExc_RuntimeError,
471                    "PyList_New() failed" );
472         
473         for (i=0, loop=self->prop->data.group.first; loop; loop=loop->next, i++) {
474                 PyList_SetItem(seq, i, BPy_IDGroup_WrapData(self->id, loop));
475         }
476         
477         return seq;
478 }
479
480 PyObject *BPy_IDGroup_HasKey(BPy_IDProperty *self, PyObject *vars)
481 {
482         IDProperty *loop;
483         char *name;
484         
485         if (!PyArg_ParseTuple(vars, "s", &name))
486                 return EXPP_ReturnPyObjError( PyExc_TypeError,
487                    "expected a string");
488                    
489         for (loop=self->prop->data.group.first; loop; loop=loop->next) {
490                 if (BSTR_EQ(loop->name, name)) Py_RETURN_TRUE;
491         }
492         
493         Py_RETURN_FALSE;
494 }
495
496 PyObject *BPy_IDGroup_Update(BPy_IDProperty *self, PyObject *vars)
497 {
498         PyObject *pyob, *pkey, *pval;
499         int i=0;
500         
501         if (PySequence_Size(vars) != 1)
502                 return EXPP_ReturnPyObjError( PyExc_TypeError,
503                    "expected an object derived from dict.");
504           
505         pyob = PyTuple_GET_ITEM(vars, 0);
506         if (!PyDict_Check(pyob))
507                 return EXPP_ReturnPyObjError( PyExc_TypeError,
508                    "expected an object derived from dict.");
509                    
510         while (PyDict_Next(pyob, &i, &pkey, &pval)) {
511                 BPy_IDGroup_Map_SetItem(self, pkey, pval);
512                 if (PyErr_Occurred()) return NULL;
513         }
514         
515         Py_RETURN_NONE;
516 }
517
518 PyObject *BPy_IDGroup_ConvertToPy(BPy_IDProperty *self)
519 {
520         return BPy_IDGroup_MapDataToPy(self->prop);
521 }
522
523 static struct PyMethodDef BPy_IDGroup_methods[] = {
524         {"pop", (PyCFunction)BPy_IDGroup_Pop, METH_VARARGS,
525                 "pop an item from the group; raises KeyError if the item doesn't exist."},
526         {"iteritems", (PyCFunction)BPy_IDGroup_IterItems, METH_NOARGS,
527                 "iterate through the items in the dict; behaves like dictionary method iteritems."},
528         {"keys", (PyCFunction)BPy_IDGroup_GetKeys, METH_NOARGS,
529                 "get the keys associated with this group as a list of strings."},
530         {"values", (PyCFunction)BPy_IDGroup_GetValues, METH_NOARGS,
531                 "get the values associated with this group."},
532         {"has_key", (PyCFunction)BPy_IDGroup_HasKey, METH_VARARGS,
533                 "returns true if the group contains a key, false if not."},
534         {"update", (PyCFunction)BPy_IDGroup_Update, METH_VARARGS,
535                 "updates the values in the group with the values of another or a dict."},
536         {"convert_to_pyobject", (PyCFunction)BPy_IDGroup_ConvertToPy, METH_NOARGS,
537                 "return a purely python version of the group."},
538         {0, NULL, 0, NULL}
539 };
540                 
541 PyMappingMethods BPy_IDGroup_Mapping = {
542         (inquiry)BPy_IDGroup_Map_Len,                   /*inquiry mp_length */
543         (binaryfunc)BPy_IDGroup_Map_GetItem,            /*binaryfunc mp_subscript */
544         (objobjargproc)BPy_IDGroup_Map_SetItem, /*objobjargproc mp_ass_subscript */
545 };
546
547 PyTypeObject IDGroup_Type = {
548         PyObject_HEAD_INIT( NULL )  /* required py macro */
549         0,                          /* ob_size */
550         /*  For printing, in format "<module>.<name>" */
551         "Blender IDProperty",           /* char *tp_name; */
552         sizeof( BPy_IDProperty ),       /* int tp_basicsize; */
553         0,                          /* tp_itemsize;  For allocation */
554
555         /* Methods to implement standard operations */
556
557         ( destructor ) IDGroup_dealloc,/* destructor tp_dealloc; */
558         NULL,                       /* printfunc tp_print; */
559         NULL,     /* getattrfunc tp_getattr; */
560         NULL,     /* setattrfunc tp_setattr; */
561         NULL,                       /* cmpfunc tp_compare; */
562         ( reprfunc ) IDGroup_repr,     /* reprfunc tp_repr; */
563
564         /* Method suites for standard classes */
565
566         NULL,                       /* PyNumberMethods *tp_as_number; */
567         NULL,                                   /* PySequenceMethods *tp_as_sequence; */
568         &BPy_IDGroup_Mapping,     /* PyMappingMethods *tp_as_mapping; */
569
570         /* More standard operations (here for binary compatibility) */
571
572         NULL,                       /* hashfunc tp_hash; */
573         NULL,                       /* ternaryfunc tp_call; */
574         NULL,                       /* reprfunc tp_str; */
575         NULL,                       /* getattrofunc tp_getattro; */
576         NULL,                       /* setattrofunc tp_setattro; */
577
578         /* Functions to access object as input/output buffer */
579         NULL,                       /* PyBufferProcs *tp_as_buffer; */
580
581   /*** Flags to define presence of optional/expanded features ***/
582         Py_TPFLAGS_DEFAULT,         /* long tp_flags; */
583
584         NULL,                       /*  char *tp_doc;  Documentation string */
585   /*** Assigned meaning in release 2.0 ***/
586         /* call function for all accessible objects */
587         NULL,                       /* traverseproc tp_traverse; */
588
589         /* delete references to contained objects */
590         NULL,                       /* inquiry tp_clear; */
591
592   /***  Assigned meaning in release 2.1 ***/
593   /*** rich comparisons ***/
594         NULL,                       /* richcmpfunc tp_richcompare; */
595
596   /***  weak reference enabler ***/
597         0,                          /* long tp_weaklistoffset; */
598
599   /*** Added in release 2.2 ***/
600         /*   Iterators */
601         (getiterfunc)BPy_IDGroup_SpawnIterator, /* getiterfunc tp_iter; */
602         NULL,                       /* iternextfunc tp_iternext; */
603   /*** Attribute descriptor and subclassing stuff ***/
604         BPy_IDGroup_methods,        /* struct PyMethodDef *tp_methods; */
605         NULL,                       /* struct PyMemberDef *tp_members; */
606         BPy_IDGroup_getseters,       /* struct PyGetSetDef *tp_getset; */
607 };
608
609 /*********** Main external wrapping function *******/
610 PyObject *BPy_Wrap_IDProperty(ID *id, IDProperty *prop, IDProperty *parent)
611 {
612         BPy_IDProperty *wrap = PyObject_New(BPy_IDProperty, &IDGroup_Type);
613         
614         if (!wrap)
615                 return EXPP_ReturnPyObjError( PyExc_RuntimeError,
616                    "PyObject_New() failed" );
617                                                    
618         wrap->prop = prop;
619         wrap->parent = parent;
620         wrap->id = id;
621         //wrap->destroy = 0;
622         return (PyObject*) wrap;
623 }
624
625
626 /********Array Wrapper********/
627
628 void IDArray_dealloc(void *self)
629 {
630         PyObject_DEL(self);
631 }
632
633 PyObject *IDArray_repr(BPy_IDArray *self)
634 {
635         return Py_BuildValue("s", "(ID Array)");
636 }
637
638
639 PyObject *BPy_IDArray_GetType(BPy_IDArray *self)
640 {
641         return Py_BuildValue("i", self->prop->subtype);
642 }
643
644 PyObject *BPy_IDArray_GetLen(BPy_IDArray *self)
645 {
646         return Py_BuildValue("i", self->prop->len);
647 }
648
649 static PyGetSetDef BPy_IDArray_getseters[] = {
650         {"len",
651          (getter)BPy_IDArray_GetLen, (setter)NULL,
652          "The length of the array, can also be gotten with len(array).",
653          NULL},
654         {"type",
655          (getter)BPy_IDArray_GetType, (setter)NULL,
656          "The type of the data in the array, is an ant.",
657          NULL}, 
658         {NULL, NULL, NULL, NULL, NULL},
659 };
660
661 int BPy_IDArray_Len(BPy_IDArray *self)
662 {
663         return self->prop->len;
664 }
665
666 PyObject *BPy_IDArray_GetItem(BPy_IDArray *self, int index)
667 {
668         if (index < 0 || index >= self->prop->len)
669                 return EXPP_ReturnPyObjError( PyExc_IndexError,
670                                 "index out of range!");
671
672         switch (self->prop->subtype) {
673                 case IDP_FLOAT:
674                         return Py_BuildValue("f", ((float*)self->prop->data.pointer)[index]);
675                         break;
676                 case IDP_INT:
677                         return Py_BuildValue("i", ((int*)self->prop->data.pointer)[index]);
678                         break;
679         }
680                 return EXPP_ReturnPyObjError( PyExc_RuntimeError,
681                                 "invalid/corrupt array type!");
682 }
683
684 int BPy_IDArray_SetItem(BPy_IDArray *self, int index, PyObject *val)
685 {
686         int i;
687         float f;
688
689         if (index < 0 || index >= self->prop->len)
690                 return EXPP_ReturnIntError( PyExc_RuntimeError,
691                                 "index out of range!");
692
693         switch (self->prop->subtype) {
694                 case IDP_FLOAT:
695                         if (!PyNumber_Check(val)) return EXPP_ReturnIntError( PyExc_TypeError,
696                                 "expected a float");
697                         val = PyNumber_Float(val);
698                         if (!val) return EXPP_ReturnIntError( PyExc_TypeError,
699                                 "expected a float");
700
701                         f = (float) PyFloat_AsDouble(val);
702                         ((float*)self->prop->data.pointer)[index] = f;
703                         Py_XDECREF(val);
704                         break;
705                 case IDP_INT:
706                         if (!PyNumber_Check(val)) return EXPP_ReturnIntError( PyExc_TypeError,
707                                 "expected an int");
708                         val = PyNumber_Int(val);
709                         if (!val) return EXPP_ReturnIntError( PyExc_TypeError,
710                                 "expected an int");
711
712                         i = (int) PyInt_AsLong(val);
713                         ((int*)self->prop->data.pointer)[index] = i;
714                         Py_XDECREF(val);
715                         break;
716         }
717         return 0;
718 }
719
720 static PySequenceMethods BPy_IDArray_Seq = {
721         (inquiry) BPy_IDArray_Len,                      /* inquiry sq_length */
722         0,                                                                      /* binaryfunc sq_concat */
723         0,                                                                      /* intargfunc sq_repeat */
724         (intargfunc)BPy_IDArray_GetItem,        /* intargfunc sq_item */
725         0,                                                                      /* intintargfunc sq_slice */
726         (intobjargproc)BPy_IDArray_SetItem,     /* intobjargproc sq_ass_item */
727         0,                                                                      /* intintobjargproc sq_ass_slice */
728         0,                                                                      /* objobjproc sq_contains */
729                                 /* Added in release 2.0 */
730         0,                                                                      /* binaryfunc sq_inplace_concat */
731         0,                                                                      /* intargfunc sq_inplace_repeat */
732 };
733
734 PyTypeObject IDArray_Type = {
735         PyObject_HEAD_INIT( NULL )  /* required py macro */
736         0,                          /* ob_size */
737         /*  For printing, in format "<module>.<name>" */
738         "Blender IDArray",           /* char *tp_name; */
739         sizeof( BPy_IDArray ),       /* int tp_basicsize; */
740         0,                          /* tp_itemsize;  For allocation */
741
742         /* Methods to implement standard operations */
743
744         ( destructor ) IDArray_dealloc,/* destructor tp_dealloc; */
745         NULL,                       /* printfunc tp_print; */
746         NULL,     /* getattrfunc tp_getattr; */
747         NULL,     /* setattrfunc tp_setattr; */
748         NULL,                       /* cmpfunc tp_compare; */
749         ( reprfunc ) IDArray_repr,     /* reprfunc tp_repr; */
750
751         /* Method suites for standard classes */
752
753         NULL,                       /* PyNumberMethods *tp_as_number; */
754         &BPy_IDArray_Seq,                       /* PySequenceMethods *tp_as_sequence; */
755         NULL,                       /* PyMappingMethods *tp_as_mapping; */
756
757         /* More standard operations (here for binary compatibility) */
758
759         NULL,                       /* hashfunc tp_hash; */
760         NULL,                       /* ternaryfunc tp_call; */
761         NULL,                       /* reprfunc tp_str; */
762         NULL,                       /* getattrofunc tp_getattro; */
763         NULL,                       /* setattrofunc tp_setattro; */
764
765         /* Functions to access object as input/output buffer */
766         NULL,                       /* PyBufferProcs *tp_as_buffer; */
767
768   /*** Flags to define presence of optional/expanded features ***/
769         Py_TPFLAGS_DEFAULT,         /* long tp_flags; */
770
771         NULL,                       /*  char *tp_doc;  Documentation string */
772   /*** Assigned meaning in release 2.0 ***/
773         /* call function for all accessible objects */
774         NULL,                       /* traverseproc tp_traverse; */
775
776         /* delete references to contained objects */
777         NULL,                       /* inquiry tp_clear; */
778
779   /***  Assigned meaning in release 2.1 ***/
780   /*** rich comparisons ***/
781         NULL,                       /* richcmpfunc tp_richcompare; */
782
783   /***  weak reference enabler ***/
784         0,                          /* long tp_weaklistoffset; */
785
786   /*** Added in release 2.2 ***/
787         /*   Iterators */
788         NULL,                       /* getiterfunc tp_iter; */
789         NULL,                       /* iternextfunc tp_iternext; */
790
791   /*** Attribute descriptor and subclassing stuff ***/
792         NULL,                       /* struct PyMethodDef *tp_methods; */
793         NULL,                       /* struct PyMemberDef *tp_members; */
794         BPy_IDArray_getseters,       /* struct PyGetSetDef *tp_getset; */
795         NULL,                       /* struct _typeobject *tp_base; */
796         NULL,                       /* PyObject *tp_dict; */
797         NULL,                       /* descrgetfunc tp_descr_get; */
798         NULL,                       /* descrsetfunc tp_descr_set; */
799         0,                          /* long tp_dictoffset; */
800         NULL,                       /* initproc tp_init; */
801         NULL,                       /* allocfunc tp_alloc; */
802         NULL,                       /* newfunc tp_new; */
803         /*  Low-level free-memory routine */
804         NULL,                       /* freefunc tp_free;  */
805         /* For PyObject_IS_GC */
806         NULL,                       /* inquiry tp_is_gc;  */
807         NULL,                       /* PyObject *tp_bases; */
808         /* method resolution order */
809         NULL,                       /* PyObject *tp_mro;  */
810         NULL,                       /* PyObject *tp_cache; */
811         NULL,                       /* PyObject *tp_subclasses; */
812         NULL,                       /* PyObject *tp_weaklist; */
813         NULL
814 };
815
816 /*********** ID Property Group iterator ********/
817 void IDGroup_Iter_dealloc(void *self)
818 {
819         PyObject_DEL(self);
820 }
821
822 PyObject *IDGroup_Iter_iterself(PyObject *self)
823 {
824         Py_XINCREF(self);
825         return self;
826 }
827
828 PyObject *IDGroup_Iter_repr(BPy_IDGroup_Iter *self)
829 {
830         return Py_BuildValue("s", "(ID Property Group)");
831 }
832
833 PyObject *BPy_Group_Iter_Next(BPy_IDGroup_Iter *self)
834 {
835         IDProperty *cur=NULL;
836
837         if (self->cur) {
838                 cur = self->cur;
839                 self->cur = self->cur->next;
840                 if (self->mode == IDPROP_ITER_ITEMS) {
841                         return Py_BuildValue("[s, O]", cur->name, BPy_IDGroup_WrapData(self->group->id, cur));
842                 } else {
843                         return Py_BuildValue("s", cur->name);
844                 }
845         } else {
846                 return EXPP_ReturnPyObjError( PyExc_StopIteration,
847                                 "iterator at end" );
848         }
849 }
850
851 PyTypeObject IDGroup_Iter_Type = {
852         PyObject_HEAD_INIT( NULL )  /* required py macro */
853         0,                          /* ob_size */
854         /*  For printing, in format "<module>.<name>" */
855         "Blender IDGroup_Iter",           /* char *tp_name; */
856         sizeof( BPy_IDGroup_Iter ),       /* int tp_basicsize; */
857         0,                          /* tp_itemsize;  For allocation */
858
859         /* Methods to implement standard operations */
860
861         ( destructor ) IDGroup_Iter_dealloc,/* destructor tp_dealloc; */
862         NULL,                       /* printfunc tp_print; */
863         NULL,     /* getattrfunc tp_getattr; */
864         NULL,     /* setattrfunc tp_setattr; */
865         NULL,                       /* cmpfunc tp_compare; */
866         ( reprfunc ) IDGroup_Iter_repr,     /* reprfunc tp_repr; */
867
868         /* Method suites for standard classes */
869
870         NULL,                       /* PyNumberMethods *tp_as_number; */
871         NULL,                                   /* PySequenceMethods *tp_as_sequence; */
872         NULL,                       /* PyMappingMethods *tp_as_mapping; */
873
874         /* More standard operations (here for binary compatibility) */
875
876         NULL,                       /* hashfunc tp_hash; */
877         NULL,                       /* ternaryfunc tp_call; */
878         NULL,                       /* reprfunc tp_str; */
879         NULL,                       /* getattrofunc tp_getattro; */
880         NULL,                       /* setattrofunc tp_setattro; */
881
882         /* Functions to access object as input/output buffer */
883         NULL,                       /* PyBufferProcs *tp_as_buffer; */
884
885   /*** Flags to define presence of optional/expanded features ***/
886         Py_TPFLAGS_DEFAULT,         /* long tp_flags; */
887
888         NULL,                       /*  char *tp_doc;  Documentation string */
889   /*** Assigned meaning in release 2.0 ***/
890         /* call function for all accessible objects */
891         NULL,                       /* traverseproc tp_traverse; */
892
893         /* delete references to contained objects */
894         NULL,                       /* inquiry tp_clear; */
895
896   /***  Assigned meaning in release 2.1 ***/
897   /*** rich comparisons ***/
898         NULL,                       /* richcmpfunc tp_richcompare; */
899
900   /***  weak reference enabler ***/
901         0,                          /* long tp_weaklistoffset; */
902
903   /*** Added in release 2.2 ***/
904         /*   Iterators */
905         IDGroup_Iter_iterself,              /* getiterfunc tp_iter; */
906         (iternextfunc) BPy_Group_Iter_Next, /* iternextfunc tp_iternext; */
907 };
908
909 void IDProp_Init_Types(void)
910 {
911         PyType_Ready( &IDGroup_Type );
912         PyType_Ready( &IDGroup_Iter_Type );
913         PyType_Ready( &IDArray_Type );
914 }