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