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