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