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