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