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