diff options
author | Joseph Eagar <joeedh@gmail.com> | 2006-11-17 07:46:48 +0300 |
---|---|---|
committer | Joseph Eagar <joeedh@gmail.com> | 2006-11-17 07:46:48 +0300 |
commit | 8768707610fbc1cea2bde069cdfd6d3f3e2fc522 (patch) | |
tree | fb8347ad36f60dede2883b587688184c3631cbcc /source | |
parent | 24f4440d05aa626f35c27758698af396ea94ff76 (diff) |
=ID Properties=
This commit adds supports for per-ID properties to blender.
See http://mediawiki.blender.org/index.php/BlenderDev/ID_Property
for more information on how it all works.
ID properties are accesable by python; but note that
bindings have only been added to Object and Material thus
far. However adding more bindings is easy and I plan
on adding several more hopefully within an hour of this inital
commit.
A generic UI panel is also planned, that will go wherever its
needed; for example in the material buttons, editing buttons, etc.
I'll likely submit the initial code for that as a patch, though,
so matt and ton and others can go over it and make sure it's
all good. :)
VERY important, if you intend to use ID properties please
go to http://mediawiki.blender.org/index.php/BlenderDev/PropertyStandards
and start writing the appropriate standards for it.
Diffstat (limited to 'source')
-rw-r--r-- | source/blender/blenkernel/BKE_idprop.h | 112 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/idprop.c | 338 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/library.c | 5 | ||||
-rw-r--r-- | source/blender/blenloader/intern/readfile.c | 98 | ||||
-rw-r--r-- | source/blender/blenloader/intern/writefile.c | 65 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_ID.h | 60 | ||||
-rw-r--r-- | source/blender/python/api2_2x/IDProp.c | 1007 | ||||
-rw-r--r-- | source/blender/python/api2_2x/IDProp.h | 26 | ||||
-rw-r--r-- | source/blender/python/api2_2x/Material.c | 13 | ||||
-rw-r--r-- | source/blender/python/api2_2x/Object.c | 14 | ||||
-rw-r--r-- | source/blender/python/api2_2x/doc/IDProp.py | 65 |
11 files changed, 1794 insertions, 9 deletions
diff --git a/source/blender/blenkernel/BKE_idprop.h b/source/blender/blenkernel/BKE_idprop.h new file mode 100644 index 00000000000..d61d274dd36 --- /dev/null +++ b/source/blender/blenkernel/BKE_idprop.h @@ -0,0 +1,112 @@ +#ifndef _BKE_IDPROP_H +#define _BKE_IDPROP_H + +#include "DNA_ID.h" + +/* +these two are included for their (new :P )function +pointers. +*/ +#include "BLO_readfile.h" +#include "BLO_writefile.h" + +struct WriteData; +struct FileData; + +struct IDProperty; +struct ID; + +typedef union { + int i; + float f; + char *str; + struct ID *id; + struct { + short type; + short len; + } array; + struct { + int matvec_size; + float *example; + } matrix_or_vector; +} IDPropertyTemplate; + +/* ----------- Array Type ----------- */ +/*this function works for strings too!*/ +void IDP_ResizeArray(struct IDProperty *prop, int newlen); +void IDP_FreeArray(struct IDProperty *prop); +void IDP_UnlinkArray(struct IDProperty *prop); + +/* ---------- String Type ------------ */ +void IDP_AssignString(struct IDProperty *prop, char *st); +void IDP_ConcatStringC(struct IDProperty *prop, char *st); +void IDP_ConcatString(struct IDProperty *str1, struct IDProperty *append); +void IDP_FreeString(struct IDProperty *prop); + +/*-------- ID Type -------*/ +void IDP_LinkID(struct IDProperty *prop, ID *id); +void IDP_UnlinkID(struct IDProperty *prop); + +/*-------- Group Functions -------*/ +void IDP_AddToGroup(struct IDProperty *group, struct IDProperty *prop); +void IDP_RemFromGroup(struct IDProperty *group, struct IDProperty *prop); +IDProperty *IDP_GetPropertyFromGroup(struct IDProperty *prop, char *name); + +/*Get an iterator to iterate over the members of an id property group. + Note that this will automatically free the iterator once iteration is complete; + if you stop the iteration before hitting the end, make sure to call + IDP_FreeIterBeforeEnd().*/ +void *IDP_GetGroupIterator(struct IDProperty *prop); + +/*Returns the next item in the iteration. To use, simple for a loop like the following: + while (IDP_GroupIterNext(iter) != NULL) { + . . . + }*/ +void *IDP_GroupIterNext(void *vself); + +/*Frees the iterator pointed to at vself, only use this if iteration is stopped early; + when the iterator hits the end of the list it'll automatially free itself.*/ +void IDP_FreeIterBeforeEnd(void *vself); + +/*-------- Main Functions --------*/ +/*Get the Group property that contains the id properties for ID id. Set create_if_needed + to create the Group property and attach it to id if it doesn't exist; otherwise + the function will return NULL if there's no Group property attached to the ID.*/ +struct IDProperty *IDP_GetProperties(struct ID *id, int create_if_needed); + +/* +Allocate a new ID. + +This function takes three arguments: the ID property type, a union which defines +it's initial value, and a name. + +The union is simple to use; see the top of this header file for its definition. +An example of using this function: + + IDPropertyTemplate val; + IDProperty *group, *idgroup, *color; + group = IDP_New(IDP_GROUP, val, "group1"); //groups don't need a template. + + val.array.len = 4 + val.array.type = IDP_FLOAT; + color = IDP_New(IDP_ARRAY, val, "color1"); + + idgroup = IDP_GetProperties(some_id, 1); + IDP_AddToGroup(idgroup, color); + IDP_AddToGroup(idgroup, group); + +Note that you MUST either attach the id property to an id property group with +IDP_AddToGroup or MEM_freeN the property, doing anything else might result in +a memory leak. +*/ +struct IDProperty *IDP_New(int type, IDPropertyTemplate val, char *name); +\ +/*NOTE: this will free all child properties of list arrays and groups! + Also, note that this does NOT unlink anything! Plus it doesn't free + the actual struct IDProperty struct either.*/ +void IDP_FreeProperty(struct IDProperty *prop); + +/*Unlinks any struct IDProperty<->ID linkage that might be going on.*/ +void IDP_UnlinkProperty(struct IDProperty *prop); + +#endif /* _BKE_IDPROP_H */ diff --git a/source/blender/blenkernel/intern/idprop.c b/source/blender/blenkernel/intern/idprop.c new file mode 100644 index 00000000000..ade4176eeac --- /dev/null +++ b/source/blender/blenkernel/intern/idprop.c @@ -0,0 +1,338 @@ +#include "DNA_listBase.h" +#include "DNA_ID.h" + +#include "BKE_idprop.h" +#include "BKE_global.h" +#include "BKE_library.h" +#include "BKE_utildefines.h" + +#include "BLI_blenlib.h" + +#include "MEM_guardedalloc.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +/* IDPropertyTemplate is a union in DNA_ID.h */ +static char idp_size_table[] = { + 0, /*strings don't have fixed sizes :)*/ + sizeof(int), + sizeof(float), + sizeof(float)*3, /*Vector type*/ + sizeof(float)*16, /*Matrix type, we allocate max 4x4 even if in 3x3 mode*/ + 0, /*arrays don't have a fixed size either :)*/ + sizeof(ListBase), /*Group type*/ + sizeof(void*) +}; + + +/* ----------- Array Type ----------- */ + +/*this function works for strings too!*/ +void IDP_ResizeArray(IDProperty *prop, int newlen) +{ + void *newarr; + int newsize=newlen; + + /*first check if the array buffer size has room*/ + /*if newlen is 200 chars less then totallen, reallocate anyway*/ + if (newlen <= prop->totallen && prop->totallen - newlen < 200) { + prop->len = newlen; + return; + } + + /* - Note: This code comes from python, here's the corrusponding comment. - */ + /* This over-allocates proportional to the list size, making room + * for additional growth. The over-allocation is mild, but is + * enough to give linear-time amortized behavior over a long + * sequence of appends() in the presence of a poorly-performing + * system realloc(). + * The growth pattern is: 0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ... + */ + newsize = (newsize >> 3) + (newsize < 9 ? 3 : 6) + newsize; + + newarr = MEM_callocN(idp_size_table[prop->type]*newsize, "idproperty array resized"); + /*newlen is bigger*/ + if (newlen >= prop->len) memcpy(newarr, prop->data.pointer, prop->len*idp_size_table[prop->type]); + /*newlen is smaller*/ + else memcpy(newarr, prop->data.pointer, newlen*prop->len*idp_size_table[prop->type]); + + MEM_freeN(prop->data.pointer); + prop->data.pointer = newarr; + prop->len = newlen; + prop->totallen = newsize; +} + +/*does NOT unlink anything!*/ +/*Ok, the way things work, Groups and List Arrays free the ID Property structs of their children. + + This is because all ID Property freeing functions free only direct data (not the ID Property + struct itself), but for Groups and List Arrays their child properties *are* considered + direct data. +*/ + void IDP_FreeArray(IDProperty *prop) +{ + if (prop->data.pointer) + MEM_freeN(prop->data.pointer); +} + +/*taken from readfile.c*/ +#define SWITCH_LONGINT(a) { \ + char s_i, *p_i; \ + p_i= (char *)&(a); \ + s_i=p_i[0]; p_i[0]=p_i[7]; p_i[7]=s_i; \ + s_i=p_i[1]; p_i[1]=p_i[6]; p_i[6]=s_i; \ + s_i=p_i[2]; p_i[2]=p_i[5]; p_i[5]=s_i; \ + s_i=p_i[3]; p_i[3]=p_i[4]; p_i[4]=s_i; } + + + +/* ---------- String Type ------------ */ +void IDP_AssignString(IDProperty *prop, char *st) +{ + int stlen; + + stlen = strlen(st); + + IDP_ResizeArray(prop, stlen+1); /*make room for null byte :) */ + strcpy(prop->data.pointer, st); +} + +void IDP_ConcatStringC(IDProperty *prop, char *st) +{ + int newlen; + + newlen = prop->len + strlen(st); + /*we have to remember that prop->len includes the null byte for strings. + so there's no need to add +1 to the resize function.*/ + IDP_ResizeArray(prop, newlen); + strcat(prop->data.pointer, st); +} + +void IDP_ConcatString(IDProperty *str1, IDProperty *append) +{ + int newlen; + + /*since ->len for strings includes the NULL byte, we have to subtract one or + we'll get an extra null byte after each concatination operation.*/ + newlen = str1->len + append->len - 1; + IDP_ResizeArray(str1, newlen); + strcat(str1->data.pointer, append->data.pointer); +} + +void IDP_FreeString(IDProperty *prop) +{ + MEM_freeN(prop->data.pointer); +} + + +/*-------- ID Type -------*/ + +void IDP_LinkID(IDProperty *prop, ID *id) +{ + if (prop->data.pointer) ((ID*)prop->data.pointer)->us--; + prop->data.pointer = id; + id_us_plus(id); +} + +void IDP_UnlinkID(IDProperty *prop) +{ + ((ID*)prop->data.pointer)->us--; +} + +/*-------- Group Functions -------*/ +void IDP_AddToGroup(IDProperty *group, IDProperty *prop) +{ + group->len++; + BLI_addtail(&group->data.group, prop); +} + +void IDP_RemFromGroup(IDProperty *group, IDProperty *prop) +{ + group->len--; + BLI_remlink(&group->data.group, prop); +} + +IDProperty *IDP_GetPropertyFromGroup(IDProperty *prop, char *name) +{ + IDProperty *loop; + for (loop=prop->data.group.first; loop; loop=loop->next) { + if (strcmp(prop->name, name)==0) return prop; + } + return NULL; +} + +typedef struct IDPIter { + void *next; + IDProperty *parent; +} IDPIter; + +void *IDP_GetGroupIterator(IDProperty *prop) +{ + IDPIter *iter = MEM_callocN(sizeof(IDPIter), "IDPIter"); + iter->next = prop->data.group.first; + iter->parent = prop; + return (void*) iter; +} + +void *IDP_GroupIterNext(void *vself) +{ + IDPIter *self = (IDPIter*) vself; + Link *next = (Link*) self->next; + if (self->next == NULL) { + MEM_freeN(self); + return NULL; + } + + self->next = next->next; + return (void*) next; +} + +void IDP_FreeIterBeforeEnd(void *vself) +{ + MEM_freeN(vself); +} + +/*Ok, the way things work, Groups and List Arrays free the ID Property structs of their children. + This is because all ID Property freeing functions free only direct data (not the ID Property + struct itself), but for Groups and List Arrays their child properties *are* considered + direct data.*/ +void IDP_FreeGroup(IDProperty *prop) +{ + IDProperty *loop, *next; + for (loop=prop->data.group.first; loop; loop=next) + { + next = loop->next; + IDP_FreeProperty(loop); + MEM_freeN(loop); + } +} + + +/*-------- Main Functions --------*/ + +IDProperty *IDP_GetProperties(ID *id, int create_if_needed) +{ + if (id->properties) return id->properties; + else { + if (create_if_needed) { + id->properties = MEM_callocN(sizeof(IDProperty), "IDProperty"); + id->properties->type = IDP_GROUP; + } + return id->properties; + } +} + +IDProperty *IDP_New(int type, IDPropertyTemplate val, char *name) +{ + IDProperty *prop=NULL; + + switch (type) { + case IDP_INT: + prop = MEM_callocN(sizeof(IDProperty), "IDProperty int"); + prop->data.val = val.i; + break; + case IDP_FLOAT: + prop = MEM_callocN(sizeof(IDProperty), "IDProperty float"); + *(float*)&prop->data.val = val.f; + break; + case IDP_ARRAY: + { + /*for now, we only support float and int arrays*/ + if (val.array.type == IDP_FLOAT || val.array.type == IDP_INT) { + prop = MEM_callocN(sizeof(IDProperty), "IDProperty array"); + prop->len = prop->totallen = val.array.len; + prop->subtype = val.array.type; + prop->data.pointer = MEM_callocN(idp_size_table[val.array.type]*val.array.len, "id property array"); + break; + } else { + return NULL; + } + } + case IDP_STRING: + { + char *st = val.str; + int stlen; + + prop = MEM_callocN(sizeof(IDProperty), "IDProperty string"); + if (st == NULL) { + prop->data.pointer = MEM_callocN(DEFAULT_ALLOC_FOR_NULL_STRINGS, "id property string 1"); + prop->totallen = DEFAULT_ALLOC_FOR_NULL_STRINGS; + prop->len = 1; /*NULL string, has len of 1 to account for null byte.*/ + } else { + stlen = strlen(st) + 1; + prop->data.pointer = MEM_callocN(stlen, "id property string 2"); + prop->len = prop->totallen = stlen; + strcpy(prop->data.pointer, st); + } + break; + } + case IDP_GROUP: + { + prop = MEM_callocN(sizeof(IDProperty), "IDProperty group"); + /* heh I think all needed values are set properly by calloc anyway :) */ + break; + } + case IDP_MATRIX: + prop = MEM_callocN(sizeof(IDProperty), "IDProperty array"); + if (val.matrix_or_vector.matvec_size == IDP_MATRIX4X4) + prop->data.pointer = MEM_callocN(sizeof(float)*4*4, "matrix 4x4 idproperty"); + else + prop->data.pointer = MEM_callocN(sizeof(float)*3*3, "matrix 3x3 idproperty"); + case IDP_VECTOR: + prop = MEM_callocN(sizeof(IDProperty), "IDProperty array"); + switch (val.matrix_or_vector.matvec_size) { + case IDP_VECTOR4D: + prop->data.pointer = MEM_callocN(sizeof(float)*4, "vector 4d idproperty"); + break; + case IDP_VECTOR3D: + prop->data.pointer = MEM_callocN(sizeof(float)*3, "vector 3d idproperty"); + break; + case IDP_VECTOR2D: + prop->data.pointer = MEM_callocN(sizeof(float)*2, "vector 2d idproperty"); + break; + + } + default: + { + prop = MEM_callocN(sizeof(IDProperty), "IDProperty array"); + break; + } + } + + prop->type = type; + strncpy(prop->name, name, MAX_IDPROP_NAME); + return prop; +} + +/*NOTE: this will free all child properties of list arrays and groups! + Also, note that this does NOT unlink anything! Plus it doesn't free + the actual IDProperty struct either.*/ +void IDP_FreeProperty(IDProperty *prop) +{ + switch (prop->type) { + case IDP_ARRAY: + IDP_FreeArray(prop); + break; + case IDP_STRING: + IDP_FreeString(prop); + break; + case IDP_GROUP: + IDP_FreeGroup(prop); + break; + case IDP_VECTOR: + case IDP_MATRIX: + MEM_freeN(prop->data.pointer); + break; + } +} + +/*Unlinks any IDProperty<->ID linkage that might be going on.*/ +void IDP_UnlinkProperty(IDProperty *prop) +{ + switch (prop->type) { + case IDP_ID: + IDP_UnlinkID(prop); + } +} diff --git a/source/blender/blenkernel/intern/library.c b/source/blender/blenkernel/intern/library.c index 2917853b72d..e6aa0208a5e 100644 --- a/source/blender/blenkernel/intern/library.c +++ b/source/blender/blenkernel/intern/library.c @@ -110,6 +110,7 @@ #include "BKE_node.h" #include "BKE_effect.h" #include "BKE_brush.h" +#include "BKE_idprop.h" #include "BPI_script.h" @@ -490,6 +491,10 @@ void free_libblock(ListBase *lb, void *idv) break; } + if (id->properties) { + IDP_FreeProperty(id->properties); + MEM_freeN(id->properties); + } BLI_remlink(lb, id); MEM_freeN(id); diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 924baf0c1f2..8df1040d530 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -131,6 +131,7 @@ #include "BKE_softbody.h" // sbNew() #include "BKE_texture.h" // for open_plugin_tex #include "BKE_utildefines.h" // SWITCH_INT DATA ENDB DNA1 O_BINARY GLOB USER TEST REND +#include "BKE_idprop.h" #include "BIF_butspace.h" // badlevel, for do_versions, patching event codes #include "BIF_previewrender.h" // bedlelvel, for struct RenderInfo @@ -1233,8 +1234,86 @@ static void test_pointer_array(FileData *fd, void **mat) } } -/* ************ READ Brush *************** */ +/* ************ READ ID Properties *************** */ + +void IDP_DirectLinkProperty(IDProperty *prop, int switch_endian, void *fd); +void IDP_LibLinkProperty(IDProperty *prop, int switch_endian, void *fd); + +void IDP_DirectLinkArray(IDProperty *prop, int switch_endian, void *fd) +{ + /*since we didn't save the extra string buffer, set totallen to len.*/ + prop->totallen = prop->len; + prop->data.pointer = newdataadr(fd, prop->data.pointer); + if (switch_endian) { + int i; + if (prop->subtype == IDP_INT) { + for (i=0; i<prop->len; i++) { + if (sizeof(int) == 4) { + SWITCH_INT(((int*)prop->data.pointer)[i]); + } else if (sizeof(int) == 8) { + SWITCH_LONGINT(((int*)prop->data.pointer)[i]) + } + } + } else if (prop->subtype == IDP_FLOAT) { + int i; + for (i=0; i<prop->len; i++) { + SWITCH_INT(((int*)prop->data.pointer)[i]); + } + } else printf("Unkown ID property array type! could not do endian switching!!\n"); + } +} + +void IDP_DirectLinkString(IDProperty *prop, int switch_endian, void *fd) +{ + /*since we didn't save the extra string buffer, set totallen to len.*/ + prop->totallen = prop->len; + prop->data.pointer = newdataadr(fd, prop->data.pointer); +} + +void IDP_DirectLinkGroup(IDProperty *prop, int switch_endian, void *fd) +{ + ListBase *lb = &prop->data.group; + IDProperty *loop; + + link_list(fd, lb); + + /*Link child id properties now*/ + for (loop=prop->data.group.first; loop; loop=loop->next) { + IDP_DirectLinkProperty(loop, switch_endian, fd); + } +} + +void IDP_DirectLinkProperty(IDProperty *prop, int switch_endian, void *fd) +{ + /*we pack floats and ints in an int, this may cause problems where sizeof(int) != sizeof(float)*/ + if (sizeof(void*) == 8 && switch_endian != 0 && BLO_test_64bits(fd)) { + if (prop->type == IDP_FLOAT && sizeof(int) == 8) { + /*un-longint switch it (as DNA saw a long int here) then int switch it*/ + SWITCH_LONGINT(prop->data.val); + SWITCH_INT(prop->data.val); + } + } + + switch (prop->type) { + case IDP_GROUP: + IDP_DirectLinkGroup(prop, switch_endian, fd); + break; + case IDP_STRING: + IDP_DirectLinkString(prop, switch_endian, fd); + break; + case IDP_ARRAY: + IDP_DirectLinkArray(prop, switch_endian, fd); + break; + } +} + +/*stub function*/ +void IDP_LibLinkProperty(IDProperty *prop, int switch_endian, void *fd) +{ +} + +/* ************ READ Brush *************** */ /* library brush linking after fileread */ static void lib_link_brush(FileData *fd, Main *main) { @@ -2198,6 +2277,11 @@ static void lib_link_material(FileData *fd, Main *main) ma= main->mat.first; while(ma) { if(ma->id.flag & LIB_NEEDLINK) { + /*Link ID Properties -- and copy this comment EXACTLY for easy finding + of library blocks that implement this.*/ + /*set head id properties type to IDP_GROUP; calloc kindly initilizes + all other needed values :) */ + if (ma->id.properties) IDP_LibLinkProperty(ma->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd); ma->ipo= newlibadr_us(fd, ma->id.lib, ma->ipo); ma->group= newlibadr_us(fd, ma->id.lib, ma->group); @@ -2228,6 +2312,11 @@ static void direct_link_material(FileData *fd, Material *ma) ma->mtex[a]= newdataadr(fd, ma->mtex[a]); } + if (ma->id.properties) { + ma->id.properties = newdataadr(fd, ma->id.properties); + IDP_DirectLinkProperty(ma->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd); + } + ma->ramp_col= newdataadr(fd, ma->ramp_col); ma->ramp_spec= newdataadr(fd, ma->ramp_spec); @@ -2412,6 +2501,7 @@ static void lib_link_object(FileData *fd, Main *main) ob= main->object.first; while(ob) { if(ob->id.flag & LIB_NEEDLINK) { + if (ob->id.properties) IDP_LibLinkProperty(ob->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd); ob->parent= newlibadr(fd, ob->id.lib, ob->parent); ob->track= newlibadr(fd, ob->id.lib, ob->track); @@ -2635,6 +2725,11 @@ static void direct_link_object(FileData *fd, Object *ob) ob->pose= newdataadr(fd, ob->pose); direct_link_pose(fd, ob->pose); + if (ob->id.properties) { + ob->id.properties = newdataadr(fd, ob->id.properties); + IDP_DirectLinkProperty(ob->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd); + } + link_list(fd, &ob->defbase); direct_link_nlastrips(fd, &ob->nlastrips); link_list(fd, &ob->constraintChannels); @@ -2706,6 +2801,7 @@ static void direct_link_object(FileData *fd, Object *ob) sb->bpoint= NULL; // init pointers so it gets rebuilt nicely sb->bspring= NULL; sb->scratch= NULL; + sb->keys= newdataadr(fd, sb->keys); test_pointer_array(fd, (void **)&sb->keys); diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index 2b2ba9f77bd..40facd30b2f 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -164,6 +164,7 @@ Important to know is that 'streaming' has been added to files, for Blender Publi #include "BKE_sound.h" /* ... and for samples */ #include "BKE_utildefines.h" // for defines #include "BKE_modifier.h" +#include "BKE_idprop.h" #ifdef WITH_VERSE #include "BKE_verse.h" #include "BIF_verse.h" @@ -376,6 +377,54 @@ static void writedata(WriteData *wd, int filecode, int len, void *adr) /* do not } /* *************** writing some direct data structs used in more code parts **************** */ +/*These functions are used by blender's .blend system for file saving/loading.*/ +void IDP_WriteProperty_OnlyData(IDProperty *prop, void *wd); +void IDP_WriteProperty(IDProperty *prop, void *wd); + +void IDP_WriteArray(IDProperty *prop, void *wd) +{ + /*REMEMBER to set totalen to len in the linking code!!*/ + if (prop->data.pointer) { + writedata(wd, DATA, MEM_allocN_len(prop->data.pointer), prop->data.pointer); + } +} + +void IDP_WriteString(IDProperty *prop, void *wd) +{ + /*REMEMBER to set totalen to len in the linking code!!*/ + writedata(wd, DATA, prop->len+1, prop->data.pointer); +} + +void IDP_WriteGroup(IDProperty *prop, void *wd) +{ + IDProperty *loop; + + for (loop=prop->data.group.first; loop; loop=loop->next) { + IDP_WriteProperty(loop, wd); + } +} + +/* Functions to read/write ID Properties */ +void IDP_WriteProperty_OnlyData(IDProperty *prop, void *wd) +{ + switch (prop->type) { + case IDP_GROUP: + IDP_WriteGroup(prop, wd); + break; + case IDP_STRING: + IDP_WriteString(prop, wd); + break; + case IDP_ARRAY: + IDP_WriteArray(prop, wd); + break; + } +} + +void IDP_WriteProperty(IDProperty *prop, void *wd) +{ + writestruct(wd, DATA, "IDProperty", 1, prop); + IDP_WriteProperty_OnlyData(prop, wd); +} static void write_curvemapping(WriteData *wd, CurveMapping *cumap) { @@ -405,7 +454,7 @@ static void write_nodetree(WriteData *wd, bNodeTree *ntree) write_curvemapping(wd, node->storage); else if(ntree->type==NTREE_COMPOSIT && (node->type==CMP_NODE_TIME || node->type==CMP_NODE_CURVE_VEC || node->type==CMP_NODE_CURVE_RGB)) write_curvemapping(wd, node->storage); - else + else writestruct(wd, DATA, node->typeinfo->storagename, 1, node->storage); } for(sock= node->inputs.first; sock; sock= sock->next) @@ -773,6 +822,12 @@ static void write_objects(WriteData *wd, ListBase *idbase) if(vnode) ob->vnode = (void*)vnode; #endif + /*Write ID Properties -- and copy this comment EXACTLY for easy finding + of library blocks that implement this.*/ + /*manually set head group property to IDP_GROUP, just in case it hadn't been + set yet :) */ + if (ob->id.properties) IDP_WriteProperty(ob->id.properties, wd); + /* direct data */ writedata(wd, DATA, sizeof(void *)*ob->totcol, ob->mat); write_effects(wd, &ob->effect); @@ -1147,6 +1202,12 @@ static void write_materials(WriteData *wd, ListBase *idbase) if(ma->id.us>0 || wd->current) { /* write LibData */ writestruct(wd, ID_MA, "Material", 1, ma); + + /*Write ID Properties -- and copy this comment EXACTLY for easy finding + of library blocks that implement this.*/ + /*manually set head group property to IDP_GROUP, just in case it hadn't been + set yet :) */ + if (ma->id.properties) IDP_WriteProperty(ma->id.properties, wd); for(a=0; a<MAX_MTEX; a++) { if(ma->mtex[a]) writestruct(wd, DATA, "MTex", 1, ma->mtex[a]); @@ -1424,7 +1485,7 @@ static void write_screens(WriteData *wd, ListBase *scrbase) writestruct(wd, DATA, "SpaceImage", 1, sl); if(sima->cumap) - write_curvemapping(wd, sima->cumap); + write_curvemapping(wd, sima->cumap); } else if(sl->spacetype==SPACE_IMASEL) { writestruct(wd, DATA, "SpaceImaSel", 1, sl); diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index d11b6ad567e..facd0b02db7 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -1,9 +1,9 @@ /** * blenlib/DNA_ID.h (mar-2001 nzc) * - * ID and Library types, which are fundamental for sdna, + * ID and Library types, which are fundamental for sdna, * - * $Id$ + * $Id$ * * ***** BEGIN GPL/BL DUAL LICENSE BLOCK ***** * @@ -36,21 +36,70 @@ #ifndef DNA_ID_H #define DNA_ID_H +#include "DNA_listBase.h" + #ifdef __cplusplus extern "C" { #endif -/* There's a nasty circular dependency here.... void* to the rescue! I - * really wonder why this is needed. */ - struct Library; struct FileData; +struct ID; + +typedef struct IDPropertyData { + void *pointer; + ListBase group; + int val, pad; +} IDPropertyData; + +typedef struct IDProperty { + struct IDProperty *next, *prev; + char name[32]; + char type, subtype; + short flag; + IDPropertyData data; + int len; /* array length, also (this is important!) string length + 1. + the idea is to be able to reuse array realloc functions on strings.*/ + /*totallen is total length of allocated array/string, including a buffer. + Note that the buffering is mild; the code comes from python's list implementation.*/ + int totallen; /*strings and arrays are both buffered, though the buffer isn't + saved. at least it won't be when I write that code. :)*/ + int saved; /*saved is used to indicate if this struct has been saved yet. + seemed like a good idea as a pad var was needed anyway :)*/ +} IDProperty; + +#define MAX_IDPROP_NAME 32 +#define DEFAULT_ALLOC_FOR_NULL_STRINGS 64 + +/*->type*/ +#define IDP_STRING 0 +#define IDP_INT 1 +#define IDP_FLOAT 2 +#define IDP_VECTOR 3 +#define IDP_MATRIX 4 +#define IDP_ARRAY 5 +#define IDP_GROUP 6 +#define IDP_ID 7 + +/*special types for vector, matrices and arrays + these arn't quite completely implemented, and + may be removed.*/ +#define IDP_MATRIX4X4 9 +#define IDP_MATRIX3X3 10 +#define IDP_VECTOR2D 11 +#define IDP_VECTOR3D 12 +#define IDP_VECTOR4D 13 +#define IDP_FILE 14 +/*add any future new id property types here.*/ /* watch it: Sequence has identical beginning. */ /** * ID is the first thing included in all serializable types. It * provides a common handle to place all data in double-linked lists. * */ + +/* There's a nasty circular dependency here.... void* to the rescue! I + * really wonder why this is needed. */ typedef struct ID { void *next, *prev; struct ID *newid; @@ -63,6 +112,7 @@ typedef struct ID { */ short flag; int icon_id; + IDProperty *properties; } ID; /** diff --git a/source/blender/python/api2_2x/IDProp.c b/source/blender/python/api2_2x/IDProp.c new file mode 100644 index 00000000000..06a8d75b549 --- /dev/null +++ b/source/blender/python/api2_2x/IDProp.c @@ -0,0 +1,1007 @@ +#include "DNA_ID.h" + +#include "BKE_idprop.h" + +#include "IDProp.h" +#include "gen_utils.h" + +#include "MEM_guardedalloc.h" + +#define BSTR_EQ(a, b) (*(a) == *(b) && !strcmp(a, b)) + +/*** Function to wrap ID properties ***/ +PyObject *BPy_Wrap_IDProperty(ID *id, IDProperty *prop); + +extern PyTypeObject IDArray_Type; + +/*********************** ID Property Main Wrapper Stuff ***************/ +void IDProperty_dealloc(BPy_IDProperty *self) +{ + if (self->data_wrap) { + Py_XDECREF(self->data_wrap); + } + + /*if (self->destroy) { + IDP_FreeProperty(self->prop); + MEM_freeN(self->prop); + }*/ + + PyObject_DEL(self); +} + +PyObject *IDProperty_repr(BPy_IDProperty *self) +{ + return Py_BuildValue("s", "(ID Property)"); +} + +extern PyTypeObject IDGroup_Type; + +PyObject *BPy_IDProperty_getattr(BPy_IDProperty *self, char *name) +{ + if (BSTR_EQ(name, "data")) { + switch (self->prop->type) { + case IDP_STRING: + return Py_BuildValue("s", self->prop->data.pointer); + case IDP_INT: + return Py_BuildValue("i", self->prop->data.val); + case IDP_FLOAT: + return Py_BuildValue("f", *(float*)(&self->prop->data.val)); + case IDP_GROUP: + /*blegh*/ + if (self->data_wrap) { + Py_XINCREF(self->data_wrap); + return self->data_wrap; + } else { + BPy_IDProperty *group = PyObject_New(BPy_IDProperty, &IDGroup_Type); + group->id = self->id; + group->data_wrap = NULL; + group->prop = self->prop; + Py_XINCREF(group); + self->data_wrap = (PyObject*) group; + return (PyObject*) group; + } + case IDP_ARRAY: + if (self->data_wrap) { + Py_XINCREF(self->data_wrap); + return self->data_wrap; + } else { + BPy_IDProperty *array = PyObject_New(BPy_IDProperty, &IDArray_Type); + array->id = self->id; + array->data_wrap = NULL; + array->prop = self->prop; + Py_XINCREF(array); + self->data_wrap = (PyObject*) array; + return (PyObject*) array; + } + case IDP_MATRIX: + case IDP_VECTOR: + break; + } + } else if (BSTR_EQ(name, "name")) { + return Py_BuildValue("s", self->prop->name); + } else if (BSTR_EQ(name, "type")) { + return Py_BuildValue("i", self->prop->type); + //} else if (BSTR_EQ(name, "object")) { + /*hrm the idea is here is to return the wrapped ID object. how the hell + do I do that? eek! */ + } else if (BSTR_EQ(name, "__member__")) + return Py_BuildValue("[s, s, s]", "data", "name", "type"); + + return NULL; +} + +int BPy_IDProperty_setattr(BPy_IDProperty *self, char *name, PyObject *val) +{ + if (BSTR_EQ(name, "type")) + return EXPP_ReturnIntError(PyExc_TypeError, "attempt to set read-only attribute!"); + else if (BSTR_EQ(name, "name")) { + char *st; + if (!PyString_Check(val)) + return EXPP_ReturnIntError(PyExc_TypeError, "expected a string!"); + + st = PyString_AsString(val); + if (strlen(st) >= MAX_IDPROP_NAME) + return EXPP_ReturnIntError(PyExc_TypeError, "string length cannot exceed 31 characters!"); + + strcpy(self->prop->name, st); + return 0; + } else if (BSTR_EQ(name, "data")) { + switch (self->prop->type) { + case IDP_STRING: + { + char *st; + if (!PyString_Check(val)) + return EXPP_ReturnIntError(PyExc_TypeError, "expected a string!"); + + st = PyString_AsString(val); + IDP_ResizeArray(self->prop, strlen(st)+1); + strcpy(self->prop->data.pointer, st); + return 0; + } + + case IDP_INT: + { + int ival; + if (!PyNumber_Check(val)) + return EXPP_ReturnIntError(PyExc_TypeError, "expected an int!"); + val = PyNumber_Int(val); + if (!val) + return EXPP_ReturnIntError(PyExc_TypeError, "expected an int!"); + ival = (int) PyInt_AsLong(val); + self->prop->data.val = ival; + Py_XDECREF(val); + break; + } + case IDP_FLOAT: + { + float fval; + if (!PyNumber_Check(val)) + return EXPP_ReturnIntError(PyExc_TypeError, "expected a float!"); + val = PyNumber_Float(val); + if (!val) + return EXPP_ReturnIntError(PyExc_TypeError, "expected a float!"); + fval = (float) PyFloat_AsDouble(val); + *(float*)&self->prop->data.val = fval; + Py_XDECREF(val); + break; + } + + default: + return EXPP_ReturnIntError(PyExc_AttributeError, "attempt to set read-only attribute!"); + } + return 0; + } + + return EXPP_ReturnIntError(PyExc_TypeError, "invalid attribute!"); +} + +int BPy_IDProperty_Map_Len(BPy_IDProperty *self) +{ + if (self->prop->type != IDP_GROUP) + return EXPP_ReturnIntError( PyExc_TypeError, + "len() of unsized object"); + + return self->prop->len; +} + +PyObject *BPy_IDProperty_Map_GetItem(BPy_IDProperty *self, PyObject *item) +{ + IDProperty *loop; + char *st; + + if (self->prop->type != IDP_GROUP) + return EXPP_ReturnPyObjError( PyExc_TypeError, + "unsubscriptable object"); + + if (!PyString_Check(item)) + return EXPP_ReturnPyObjError( PyExc_TypeError, + "only strings are allowed as keys of ID properties"); + + st = PyString_AsString(item); + for (loop=self->prop->data.group.first; loop; loop=loop->next) { + if (BSTR_EQ(loop->name, st)) return BPy_Wrap_IDProperty(self->id, loop); + } + return EXPP_ReturnPyObjError( PyExc_KeyError, + "key not in subgroup dict"); +} + +/*returns NULL on success, error string on failure*/ +char *BPy_IDProperty_Map_ValidateAndCreate(char *name, IDProperty *group, PyObject *ob) +{ + IDProperty *prop = NULL; + IDPropertyTemplate val; + + if (PyFloat_Check(ob)) { + val.f = (float) PyFloat_AsDouble(ob); + prop = IDP_New(IDP_FLOAT, val, name); + } else if (PyInt_Check(ob)) { + val.i = (int) PyInt_AsLong(ob); + prop = IDP_New(IDP_INT, val, name); + } else if (PyString_Check(ob)) { + val.str = PyString_AsString(ob); + prop = IDP_New(IDP_STRING, val, name); + } else if (PySequence_Check(ob)) { + PyObject *item; + int i; + + /*validate sequence and derive type. + we assume IDP_INT unless we hit a float + number; then we assume it's */ + val.array.type = IDP_INT; + val.array.len = PySequence_Length(ob); + for (i=0; i<val.array.len; i++) { + item = PySequence_GetItem(ob, i); + if (PyFloat_Check(item)) val.array.type = IDP_FLOAT; + else if (!PyInt_Check(item)) return "only floats and ints are allowed in ID property arrays"; + Py_XDECREF(item); + } + + prop = IDP_New(IDP_ARRAY, val, name); + for (i=0; i<val.array.len; i++) { + item = PySequence_GetItem(ob, i); + Py_XDECREF(item); + if (val.array.type == IDP_INT) { + item = PyNumber_Int(item); + ((int*)prop->data.pointer)[i] = (int)PyInt_AsLong(item); + } else { + item = PyNumber_Float(item); + ((float*)prop->data.pointer)[i] = (float)PyFloat_AsDouble(item); + } + Py_XDECREF(item); + } + } else if (PyMapping_Check(ob)) { + PyObject *keys, *vals, *key, *pval; + int i, len; + /*yay! we get into recursive stuff now!*/ + keys = PyMapping_Keys(ob); + vals = PyMapping_Values(ob); + + /*we allocate the group first; if we hit any invalid data, + we can delete it easily enough.*/ + prop = IDP_New(IDP_GROUP, val, name); + len = PyMapping_Length(ob); + for (i=0; i<len; i++) { + key = PySequence_GetItem(keys, i); + pval = PySequence_GetItem(vals, i); + if (!PyString_Check(key)) { + IDP_FreeProperty(prop); + MEM_freeN(prop); + Py_XDECREF(key); + Py_XDECREF(pval); + return "invalid element in subgroup dict template!"; + } + if (BPy_IDProperty_Map_ValidateAndCreate(PyString_AsString(key), prop, pval)) { + IDP_FreeProperty(prop); + MEM_freeN(prop); + Py_XDECREF(key); + Py_XDECREF(pval); + return "invalid element in subgroup dict template!"; + } + Py_XDECREF(key); + Py_XDECREF(pval); + } + Py_XDECREF(keys); + Py_XDECREF(vals); + } + + IDP_AddToGroup(group, prop); + return NULL; +} + +int BPy_IDProperty_Map_SetItem(BPy_IDProperty *self, PyObject *key, PyObject *val) +{ + char *err; + + if (self->prop->type != IDP_GROUP) + return EXPP_ReturnIntError( PyExc_TypeError, + "unsubscriptable object"); + + if (!PyString_Check(key)) + return EXPP_ReturnIntError( PyExc_TypeError, + "only strings are allowed as subgroup keys" ); + + err = BPy_IDProperty_Map_ValidateAndCreate(PyString_AsString(key), self->prop, val); + if (err) return EXPP_ReturnIntError( PyExc_RuntimeError, err ); + + return 0; +} + +PyMappingMethods BPy_IDProperty_Mapping = { + (inquiry)BPy_IDProperty_Map_Len, /*inquiry mp_length */ + (binaryfunc)BPy_IDProperty_Map_GetItem, /*binaryfunc mp_subscript */ + (objobjargproc)BPy_IDProperty_Map_SetItem, /*objobjargproc mp_ass_subscript */ +}; + +PyTypeObject IDProperty_Type = { + PyObject_HEAD_INIT( NULL ) /* required py macro */ + 0, /* ob_size */ + /* For printing, in format "<module>.<name>" */ + "Blender IDProperty", /* char *tp_name; */ + sizeof( BPy_IDProperty ), /* int tp_basicsize; */ + 0, /* tp_itemsize; For allocation */ + + /* Methods to implement standard operations */ + + ( destructor ) IDProperty_dealloc,/* destructor tp_dealloc; */ + NULL, /* printfunc tp_print; */ + (getattrfunc) BPy_IDProperty_getattr, /* getattrfunc tp_getattr; */ + (setattrfunc) BPy_IDProperty_setattr, /* setattrfunc tp_setattr; */ + NULL, /* cmpfunc tp_compare; */ + ( reprfunc ) IDProperty_repr, /* reprfunc tp_repr; */ + + /* Method suites for standard classes */ + + NULL, /* PyNumberMethods *tp_as_number; */ + NULL, /* PySequenceMethods *tp_as_sequence; */ + &BPy_IDProperty_Mapping, /* PyMappingMethods *tp_as_mapping; */ + + /* More standard operations (here for binary compatibility) */ + + NULL, /* hashfunc tp_hash; */ + NULL, /* ternaryfunc tp_call; */ + NULL, /* reprfunc tp_str; */ + NULL, /* getattrofunc tp_getattro; */ + NULL, /* setattrofunc tp_setattro; */ + + /* Functions to access object as input/output buffer */ + NULL, /* PyBufferProcs *tp_as_buffer; */ + + /*** Flags to define presence of optional/expanded features ***/ + Py_TPFLAGS_DEFAULT, /* long tp_flags; */ + + NULL, /* char *tp_doc; Documentation string */ + /*** Assigned meaning in release 2.0 ***/ + /* call function for all accessible objects */ + NULL, /* traverseproc tp_traverse; */ + + /* delete references to contained objects */ + NULL, /* inquiry tp_clear; */ + + /*** Assigned meaning in release 2.1 ***/ + /*** rich comparisons ***/ + NULL, /* richcmpfunc tp_richcompare; */ + + /*** weak reference enabler ***/ + 0, /* long tp_weaklistoffset; */ + + /*** Added in release 2.2 ***/ + /* Iterators */ + NULL, /* getiterfunc tp_iter; */ + NULL, /* iternextfunc tp_iternext; */ +}; + +/*********** Main wrapping function *******/ +PyObject *BPy_Wrap_IDProperty(ID *id, IDProperty *prop) +{ + BPy_IDProperty *wrap = PyObject_New(BPy_IDProperty, &IDProperty_Type); + wrap->prop = prop; + wrap->id = id; + wrap->data_wrap = NULL; + //wrap->destroy = 0; + return (PyObject*) wrap; +} + + +/********Array Wrapper********/ + +void IDArray_dealloc(void *self) +{ + PyObject_DEL(self); +} + +PyObject *IDArray_repr(BPy_IDArray *self) +{ + return Py_BuildValue("s", "(ID Array)"); +} + +PyObject *BPy_IDArray_getattr(BPy_IDArray *self, char *name) +{ + if (BSTR_EQ(name, "len")) return Py_BuildValue("i", self->prop->len); + else if (BSTR_EQ(name, "type")) return Py_BuildValue("i", self->prop->subtype); + else return NULL; +} + +int BPy_IDArray_setattr(BPy_IDArray *self, PyObject *val, char *name) +{ + return -1; +} + +int BPy_IDArray_Len(BPy_IDArray *self) +{ + return self->prop->len; +} + +PyObject *BPy_IDArray_GetItem(BPy_IDArray *self, int index) +{ + if (index < 0 || index >= self->prop->len) + return EXPP_ReturnPyObjError( PyExc_IndexError, + "index out of range!"); + + switch (self->prop->subtype) { + case IDP_FLOAT: + return Py_BuildValue("f", ((float*)self->prop->data.pointer)[index]); + break; + case IDP_INT: + return Py_BuildValue("i", ((int*)self->prop->data.pointer)[index]); + break; + /*case IDP_LISTARRAY: + return BPy_Wrap_IDProperty(self->id, ((IDProperty**)self->prop->data.array)[index]); + break;*/ + } + return EXPP_ReturnPyObjError( PyExc_RuntimeError, + "invalid/corrupt array type!"); +} + +int BPy_IDArray_SetItem(BPy_IDArray *self, int index, PyObject *val) +{ + int i; + float f; + + if (index < 0 || index >= self->prop->len) + return EXPP_ReturnIntError( PyExc_RuntimeError, + "index out of range!"); + + switch (self->prop->subtype) { + case IDP_FLOAT: + if (!PyNumber_Check(val)) return EXPP_ReturnIntError( PyExc_TypeError, + "expected a float"); + val = PyNumber_Float(val); + if (!val) return EXPP_ReturnIntError( PyExc_TypeError, + "expected a float"); + + f = (float) PyFloat_AsDouble(val); + ((float*)self->prop->data.pointer)[index] = f; + Py_XDECREF(val); + break; + case IDP_INT: + if (!PyNumber_Check(val)) return EXPP_ReturnIntError( PyExc_TypeError, + "expected an int"); + val = PyNumber_Int(val); + if (!val) return EXPP_ReturnIntError( PyExc_TypeError, + "expected an int"); + + i = (int) PyInt_AsLong(val); + ((int*)self->prop->data.pointer)[index] = i; + Py_XDECREF(val); + break; + /*case IDP_LISTARRAY: + if (!PyObject_TypeCheck(val, &IDProperty_Type)) + return EXPP_ReturnIntError( PyExc_TypeError, + "expected an IDProperty"); + + if (((IDProperty**)self->prop->data.array)[index]) { + IDP_FreeProperty(((IDProperty**)self->prop->data.array)[index]); + MEM_freeN(((IDProperty**)self->prop->data.array)[index]); + } + ((IDProperty**)self->prop->data.array)[index] = ((BPy_IDProperty*); + break;*/ + } + return 0; +} + +static PySequenceMethods BPy_IDArray_Seq = { + (inquiry) BPy_IDArray_Len, /* inquiry sq_length */ + 0, /* binaryfunc sq_concat */ + 0, /* intargfunc sq_repeat */ + (intargfunc)BPy_IDArray_GetItem, /* intargfunc sq_item */ + 0, /* intintargfunc sq_slice */ + (intobjargproc)BPy_IDArray_SetItem, /* intobjargproc sq_ass_item */ + 0, /* intintobjargproc sq_ass_slice */ + 0, /* objobjproc sq_contains */ + /* Added in release 2.0 */ + 0, /* binaryfunc sq_inplace_concat */ + 0, /* intargfunc sq_inplace_repeat */ +}; + +PyTypeObject IDArray_Type = { + PyObject_HEAD_INIT( NULL ) /* required py macro */ + 0, /* ob_size */ + /* For printing, in format "<module>.<name>" */ + "Blender IDArray", /* char *tp_name; */ + sizeof( BPy_IDArray ), /* int tp_basicsize; */ + 0, /* tp_itemsize; For allocation */ + + /* Methods to implement standard operations */ + + ( destructor ) IDArray_dealloc,/* destructor tp_dealloc; */ + NULL, /* printfunc tp_print; */ + ( getattrfunc ) BPy_IDArray_getattr, /* getattrfunc tp_getattr; */ + ( setattrfunc ) BPy_IDArray_setattr, /* setattrfunc tp_setattr; */ + NULL, /* cmpfunc tp_compare; */ + ( reprfunc ) IDArray_repr, /* reprfunc tp_repr; */ + + /* Method suites for standard classes */ + + NULL, /* PyNumberMethods *tp_as_number; */ + &BPy_IDArray_Seq, /* PySequenceMethods *tp_as_sequence; */ + NULL, /* PyMappingMethods *tp_as_mapping; */ + + /* More standard operations (here for binary compatibility) */ + + NULL, /* hashfunc tp_hash; */ + NULL, /* ternaryfunc tp_call; */ + NULL, /* reprfunc tp_str; */ + NULL, /* getattrofunc tp_getattro; */ + NULL, /* setattrofunc tp_setattro; */ + + /* Functions to access object as input/output buffer */ + NULL, /* PyBufferProcs *tp_as_buffer; */ + + /*** Flags to define presence of optional/expanded features ***/ + Py_TPFLAGS_DEFAULT, /* long tp_flags; */ + + NULL, /* char *tp_doc; Documentation string */ + /*** Assigned meaning in release 2.0 ***/ + /* call function for all accessible objects */ + NULL, /* traverseproc tp_traverse; */ + + /* delete references to contained objects */ + NULL, /* inquiry tp_clear; */ + + /*** Assigned meaning in release 2.1 ***/ + /*** rich comparisons ***/ + NULL, /* richcmpfunc tp_richcompare; */ + + /*** weak reference enabler ***/ + 0, /* long tp_weaklistoffset; */ + + /*** Added in release 2.2 ***/ + /* Iterators */ + NULL, /* getiterfunc tp_iter; */ + NULL, /* iternextfunc tp_iternext; */ + + /*** Attribute descriptor and subclassing stuff ***/ + NULL, /* struct PyMethodDef *tp_methods; */ + NULL, /* struct PyMemberDef *tp_members; */ + NULL, /* struct PyGetSetDef *tp_getset; */ + NULL, /* struct _typeobject *tp_base; */ + NULL, /* PyObject *tp_dict; */ + NULL, /* descrgetfunc tp_descr_get; */ + NULL, /* descrsetfunc tp_descr_set; */ + 0, /* long tp_dictoffset; */ + NULL, /* initproc tp_init; */ + NULL, /* allocfunc tp_alloc; */ + NULL, /* newfunc tp_new; */ + /* Low-level free-memory routine */ + NULL, /* freefunc tp_free; */ + /* For PyObject_IS_GC */ + NULL, /* inquiry tp_is_gc; */ + NULL, /* PyObject *tp_bases; */ + /* method resolution order */ + NULL, /* PyObject *tp_mro; */ + NULL, /* PyObject *tp_cache; */ + NULL, /* PyObject *tp_subclasses; */ + NULL, /* PyObject *tp_weaklist; */ + NULL +}; + +/*********** ID Property Group iterator ********/ +void IDGroup_Iter_dealloc(void *self) +{ + PyObject_DEL(self); +} + +PyObject *IDGroup_Iter_repr(BPy_IDGroup_Iter *self) +{ + return Py_BuildValue("s", "(ID Property Group)"); +} + +PyObject *BPy_IDGroup_Iter_getattr(BPy_IDGroup_Iter *self, char *name) +{ + return Py_BuildValue("(s)", "None!! Not implemented!!"); +} + +PyObject *BPy_IDGroup_Iter_setattr(BPy_IDGroup_Iter *self, PyObject *val, char *name) +{ + return 0; +} + +PyObject *BPy_Group_Iter_Next(BPy_IDGroup_Iter *self) +{ + IDProperty *cur=NULL; + + if (self->cur) { + cur = self->cur; + self->cur = self->cur->next; + return BPy_Wrap_IDProperty(self->group->id, cur); + } else { + return EXPP_ReturnPyObjError( PyExc_StopIteration, + "iterator at end" ); + } +} + +PyTypeObject IDGroup_Iter_Type = { + PyObject_HEAD_INIT( NULL ) /* required py macro */ + 0, /* ob_size */ + /* For printing, in format "<module>.<name>" */ + "Blender IDGroup_Iter", /* char *tp_name; */ + sizeof( BPy_IDGroup_Iter ), /* int tp_basicsize; */ + 0, /* tp_itemsize; For allocation */ + + /* Methods to implement standard operations */ + + ( destructor ) IDGroup_Iter_dealloc,/* destructor tp_dealloc; */ + NULL, /* printfunc tp_print; */ + (getattrfunc) BPy_IDGroup_Iter_getattr, /* getattrfunc tp_getattr; */ + (setattrfunc) BPy_IDGroup_Iter_setattr, /* setattrfunc tp_setattr; */ + NULL, /* cmpfunc tp_compare; */ + ( reprfunc ) IDGroup_Iter_repr, /* reprfunc tp_repr; */ + + /* Method suites for standard classes */ + + NULL, /* PyNumberMethods *tp_as_number; */ + NULL, /* PySequenceMethods *tp_as_sequence; */ + NULL, /* PyMappingMethods *tp_as_mapping; */ + + /* More standard operations (here for binary compatibility) */ + + NULL, /* hashfunc tp_hash; */ + NULL, /* ternaryfunc tp_call; */ + NULL, /* reprfunc tp_str; */ + NULL, /* getattrofunc tp_getattro; */ + NULL, /* setattrofunc tp_setattro; */ + + /* Functions to access object as input/output buffer */ + NULL, /* PyBufferProcs *tp_as_buffer; */ + + /*** Flags to define presence of optional/expanded features ***/ + Py_TPFLAGS_DEFAULT, /* long tp_flags; */ + + NULL, /* char *tp_doc; Documentation string */ + /*** Assigned meaning in release 2.0 ***/ + /* call function for all accessible objects */ + NULL, /* traverseproc tp_traverse; */ + + /* delete references to contained objects */ + NULL, /* inquiry tp_clear; */ + + /*** Assigned meaning in release 2.1 ***/ + /*** rich comparisons ***/ + NULL, /* richcmpfunc tp_richcompare; */ + + /*** weak reference enabler ***/ + 0, /* long tp_weaklistoffset; */ + + /*** Added in release 2.2 ***/ + /* Iterators */ + NULL, /* getiterfunc tp_iter; */ + (iternextfunc) BPy_Group_Iter_Next, /* iternextfunc tp_iternext; */ +}; + +/*********** ID Property Group wrapper **********/ +PyObject *BPy_IDGroup_NewProperty(BPy_IDProperty *self, PyObject *args); +PyObject *BPy_IDGroup_DeleteProperty(BPy_IDProperty *self, PyObject *args); + +static PyMethodDef BPy_IDGroup_methods[] = { + /* name, method, flags, doc */ + {"newProperty", ( PyCFunction ) BPy_IDGroup_NewProperty, METH_VARARGS, + "Create a new ID property and attach to group."}, + {"deleteProperty", ( PyCFunction ) BPy_IDGroup_DeleteProperty, METH_VARARGS, + "Delete an ID property. Takes either a string of the id property to be deleted,\n \ +or a reference to the ID property itself as an argument"}, + {NULL}, +}; + +void IDGroup_dealloc(void *self) +{ + PyObject_DEL(self); +} + +PyObject *IDGroup_repr(BPy_IDProperty *self) +{ + return Py_BuildValue("s", "(ID Property Group)"); +} + +PyObject *BPy_IDGroup_getattr(BPy_IDProperty *self, char *name) +{ + if (BSTR_EQ(name, "__members__")) return Py_BuildValue("[]"); + else return Py_FindMethod( BPy_IDGroup_methods, ( PyObject * ) self, name );; +} + +PyObject *BPy_IDGroup_setattr(BPy_IDProperty *self, PyObject *val, char *name) +{ + return 0; +} + +PyObject *BPy_IDGroup_SpawnIterator(BPy_IDProperty *self) +{ + BPy_IDGroup_Iter *iter = PyObject_New(BPy_IDGroup_Iter, &IDGroup_Iter_Type); + iter->group = self; + iter->cur = self->prop->data.group.first; + Py_XINCREF(iter); + return (PyObject*) iter; +} + +#define Default_Return "expected a string or int, a string plus variable number of additional arguments" +PyObject *BPy_IDGroup_NewProperty(BPy_IDProperty *self, PyObject *args) +{ + IDProperty *prop = NULL; + IDPropertyTemplate val = {0}; + PyObject *pyob, *pyprop=NULL; + char *name; + int nargs; + long type=0; + + /*the arguments required differs depending on the ID type. so we read + the first argument first before doing anything else. */ + nargs = PySequence_Size(args); + if (nargs < 2) { + return EXPP_ReturnPyObjError( PyExc_TypeError, + Default_Return); + } + pyob = PyTuple_GET_ITEM(args, 0); + + /*we got a string instead of a type number as argument.*/ + if (PyString_Check(pyob)) { + char *st = PyString_AsString(pyob); + if (BSTR_EQ(st, "String")) type = IDP_STRING; + else if (BSTR_EQ(st, "Group")) type = IDP_GROUP; + else if (BSTR_EQ(st, "Int")) type = IDP_INT; + else if (BSTR_EQ(st, "Float")) type = IDP_FLOAT; + else if (BSTR_EQ(st, "Array")) type = IDP_ARRAY; + else return EXPP_ReturnPyObjError( PyExc_TypeError, "invalid id property type!"); + } else { + if (!PyNumber_Check(pyob)) { + return EXPP_ReturnPyObjError( PyExc_TypeError, Default_Return); + } + pyob = PyNumber_Int(pyob); + if (pyob == NULL) { + return EXPP_ReturnPyObjError( PyExc_TypeError, Default_Return); + } + type = PyInt_AsLong(pyob); + Py_XDECREF(pyob); + } + + pyob = PyTuple_GET_ITEM(args, 1); + if (!PyString_Check(pyob)) + return EXPP_ReturnPyObjError( PyExc_TypeError, Default_Return); + + name = PyString_AsString(pyob); + //printf("name: %p %s\n", name, name); + //printf("group name: %s\n", self->prop->name); + switch (type) { + case IDP_STRING: + { + if (nargs > 3) { + return EXPP_ReturnPyObjError( PyExc_TypeError, + "expected a string or an int, a string and optionally a string"); + } + if (nargs == 3) { + val.str = PyString_AsString(PyTuple_GET_ITEM(args, 2)); + } else val.str = NULL; + + prop = IDP_New(IDP_STRING, val, name); + break; + } + case IDP_GROUP: + if (nargs != 2) { + return EXPP_ReturnPyObjError( PyExc_TypeError, + "expected a string or an int, and a string"); + } + prop = IDP_New(IDP_GROUP, val, name); + break; + case IDP_INT: + if (nargs != 2 && nargs != 3) + return EXPP_ReturnPyObjError( PyExc_TypeError, + "expected a string or an int, a string and optionally an int"); + val.i = 0; + if (nargs == 3) { + pyob = PyTuple_GET_ITEM(args, 2); + if (!PyNumber_Check(pyob)) + return EXPP_ReturnPyObjError( PyExc_TypeError, + "expected a string or an int, a string and optionally an int"); + pyob = PyNumber_Int(pyob); + if (pyob == NULL) + return EXPP_ReturnPyObjError( PyExc_TypeError, + "expected a string or an int, a string and optionally an int"); + val.i = (int) PyInt_AsLong(pyob); + Py_XDECREF(pyob); + } + prop = IDP_New(IDP_INT, val, name); + break; + case IDP_FLOAT: + if (nargs != 2 && nargs != 3) + return EXPP_ReturnPyObjError( PyExc_TypeError, + "expected a string or an int, a string and optionally an int"); + val.i = 0; + if (nargs == 3) { + pyob = PyTuple_GET_ITEM(args, 2); + if (!PyNumber_Check(pyob)) + return EXPP_ReturnPyObjError( PyExc_TypeError, + "expected a string or an int, a string and optionally an int"); + pyob = PyNumber_Float(pyob); + if (pyob == NULL) + return EXPP_ReturnPyObjError( PyExc_TypeError, + "expected a string or an int, a string and optionally an int"); + val.f = (int) PyFloat_AS_DOUBLE(pyob); + Py_XDECREF(pyob); + } + prop = IDP_New(IDP_FLOAT, val, name); + break; + case IDP_ARRAY: + { + int arrtype=0; + if (nargs != 4 && !PyNumber_Check(PyTuple_GET_ITEM(args, 2)) + && (!PyNumber_Check(PyTuple_GET_ITEM(args, 3)) || !PyString_Check(PyTuple_GET_ITEM(args, 3)))) + return EXPP_ReturnPyObjError( PyExc_TypeError, + "expected a string or an int, a string, an int or a string, and an int"); + + pyob = PyTuple_GET_ITEM(args, 2); + if (PyNumber_Check(pyob)) { + pyob = PyNumber_Int(pyob); + if (pyob == NULL) + return EXPP_ReturnPyObjError( PyExc_TypeError, + "expected a string or an int, a string, an int or a string, and an int"); + + arrtype = (int)PyInt_AsLong(pyob); + Py_XDECREF(pyob); + if (arrtype != IDP_FLOAT && arrtype != IDP_INT) + EXPP_ReturnPyObjError( PyExc_TypeError, "invalid array type constant"); + } else { + char *st = PyString_AsString(pyob); + if (BSTR_EQ(st, "Float")) arrtype = IDP_FLOAT; + else if (BSTR_EQ(st, "Int")) arrtype = IDP_INT; + else return EXPP_ReturnPyObjError( PyExc_TypeError, "invalid array type"); + } + + if (arrtype == 0) return NULL; + + val.array.type = arrtype; + pyob = PyNumber_Int(PyTuple_GET_ITEM(args, 3)); + if (pyob == NULL) + return EXPP_ReturnPyObjError( PyExc_TypeError, + "expected a string or an int, a string, an int or a string, and an int"); + + val.array.len = (int)PyInt_AsLong(pyob); + + if (val.array.len <= 0) + return EXPP_ReturnPyObjError( PyExc_TypeError, + "array len must be greater then zero!"); + + prop = IDP_New(IDP_ARRAY, val, name); + if (!prop) return EXPP_ReturnPyObjError( PyExc_RuntimeError, + "error creating array!"); + break; + } + default: + return EXPP_ReturnPyObjError( PyExc_TypeError, + "invalid id property type"); + } + + IDP_AddToGroup(self->prop, prop); + pyprop = BPy_Wrap_IDProperty(self->id, prop); + //Py_XINCREF(pyprop); + return pyprop; +} + +#undef Default_Return + +PyObject *BPy_IDGroup_DeleteProperty(BPy_IDProperty *self, PyObject *args) +{ + IDProperty *loop; + PyObject *ob; + char *name; + int nargs; + + nargs = PySequence_Size(args); + if (nargs != 1) + return EXPP_ReturnPyObjError( PyExc_TypeError, + "expected a string or IDProperty object"); + + ob = PyTuple_GET_ITEM(args, 0); + if (PyString_Check(ob)) { + name = PyString_AsString(ob); + for (loop=self->prop->data.group.first; loop; loop=loop->next) { + if (BSTR_EQ(name, loop->name)) { + IDP_RemFromGroup(self->prop, loop); + IDP_FreeProperty(loop); + MEM_freeN(loop); + Py_INCREF( Py_None ); + return Py_None; + } + } + return EXPP_ReturnPyObjError( PyExc_AttributeError, + "IDProperty not in group!"); + + } else if (PyObject_TypeCheck(ob, &IDProperty_Type)) { + for (loop=self->prop->data.group.first; loop; loop=loop->next) { + if (loop == ((BPy_IDProperty*)ob)->prop) { + IDP_RemFromGroup(self->prop, loop); + IDP_FreeProperty(loop); + MEM_freeN(loop); + Py_INCREF( Py_None ); + return Py_None; + } + } + return EXPP_ReturnPyObjError( PyExc_AttributeError, + "IDProperty not in group!"); + } + + return EXPP_ReturnPyObjError( PyExc_AttributeError, + "expected a string or IDProperty object"); +} + +int BPy_IDGroup_Len(BPy_IDArray *self) +{ + return self->prop->len; +} + +PyObject *BPy_IDGroup_GetItem(BPy_IDProperty *self, int index) +{ + IDProperty *prop; + int i; + + for (prop=self->prop->data.group.first, i=0; prop; prop=prop->next, i++) { + if (i == index) return BPy_Wrap_IDProperty(self->id, prop); + } + + return EXPP_ReturnPyObjError( PyExc_IndexError, + "index out of range!"); +} + +static PySequenceMethods BPy_IDGroup_Seq = { + (inquiry) BPy_IDGroup_Len, /* inquiry sq_length */ + 0, /* binaryfunc sq_concat */ + 0, /* intargfunc sq_repeat */ + (intargfunc)BPy_IDGroup_GetItem, /* intargfunc sq_item */ + 0, /* intintargfunc sq_slice */ + 0, /* intobjargproc sq_ass_item */ + 0, /* intintobjargproc sq_ass_slice */ + 0, /* objobjproc sq_contains */ + /* Added in release 2.0 */ + 0, /* binaryfunc sq_inplace_concat */ + 0, /* intargfunc sq_inplace_repeat */ +}; + +PyTypeObject IDGroup_Type = { + PyObject_HEAD_INIT( NULL ) /* required py macro */ + 0, /* ob_size */ + /* For printing, in format "<module>.<name>" */ + "Blender IDGroup", /* char *tp_name; */ + sizeof( BPy_IDProperty ), /* int tp_basicsize; */ + 0, /* tp_itemsize; For allocation */ + + /* Methods to implement standard operations */ + + ( destructor ) IDGroup_dealloc,/* destructor tp_dealloc; */ + NULL, /* printfunc tp_print; */ + ( getattrfunc ) BPy_IDGroup_getattr, /* getattrfunc tp_getattr; */ + ( setattrfunc ) BPy_IDGroup_setattr, /* setattrfunc tp_setattr; */ + NULL, /* cmpfunc tp_compare; */ + ( reprfunc ) IDGroup_repr, /* reprfunc tp_repr; */ + + /* Method suites for standard classes */ + + NULL, /* PyNumberMethods *tp_as_number; */ + &BPy_IDGroup_Seq, /* PySequenceMethods *tp_as_sequence; */ + &BPy_IDProperty_Mapping, /* PyMappingMethods *tp_as_mapping; */ + + /* More standard operations (here for binary compatibility) */ + + NULL, /* hashfunc tp_hash; */ + NULL, /* ternaryfunc tp_call; */ + NULL, /* reprfunc tp_str; */ + NULL, /* getattrofunc tp_getattro; */ + NULL, /* setattrofunc tp_setattro; */ + + /* Functions to access object as input/output buffer */ + NULL, /* PyBufferProcs *tp_as_buffer; */ + + /*** Flags to define presence of optional/expanded features ***/ + Py_TPFLAGS_DEFAULT, /* long tp_flags; */ + + NULL, /* char *tp_doc; Documentation string */ + /*** Assigned meaning in release 2.0 ***/ + /* call function for all accessible objects */ + NULL, /* traverseproc tp_traverse; */ + + /* delete references to contained objects */ + NULL, /* inquiry tp_clear; */ + + /*** Assigned meaning in release 2.1 ***/ + /*** rich comparisons ***/ + NULL, /* richcmpfunc tp_richcompare; */ + + /*** weak reference enabler ***/ + 0, /* long tp_weaklistoffset; */ + + /*** Added in release 2.2 ***/ + /* Iterators */ + ( getiterfunc ) BPy_IDGroup_SpawnIterator, /* getiterfunc tp_iter; */ + NULL, /* iternextfunc tp_iternext; */ + + /*** Attribute descriptor and subclassing stuff ***/ + BPy_IDGroup_methods, /* struct PyMethodDef *tp_methods; */ + NULL, /* struct PyMemberDef *tp_members; */ + NULL, /* struct PyGetSetDef *tp_getset; */ + NULL, /* struct _typeobject *tp_base; */ + NULL, /* PyObject *tp_dict; */ + NULL, /* descrgetfunc tp_descr_get; */ + NULL, /* descrsetfunc tp_descr_set; */ + 0, /* long tp_dictoffset; */ + NULL, /* initproc tp_init; */ + NULL, /* allocfunc tp_alloc; */ + NULL, /* newfunc tp_new; */ + /* Low-level free-memory routine */ + NULL, /* freefunc tp_free; */ +}; diff --git a/source/blender/python/api2_2x/IDProp.h b/source/blender/python/api2_2x/IDProp.h new file mode 100644 index 00000000000..5fdac5611f4 --- /dev/null +++ b/source/blender/python/api2_2x/IDProp.h @@ -0,0 +1,26 @@ +#include <Python.h> + +struct ID; +struct IDProperty; +struct BPy_IDGroup_Iter; + +typedef struct BPy_IDProperty { + PyObject_VAR_HEAD + struct ID *id; + struct IDProperty *prop; + PyObject *data_wrap; +} BPy_IDProperty; + +typedef struct BPy_IDArray { + PyObject_VAR_HEAD + struct ID *id; + struct IDProperty *prop; +} BPy_IDArray; + +typedef struct BPy_IDGroup_Iter { + PyObject_VAR_HEAD + BPy_IDProperty *group; + struct IDProperty *cur; +} BPy_IDGroup_Iter; + +PyObject *BPy_Wrap_IDProperty(struct ID *id, struct IDProperty *prop); diff --git a/source/blender/python/api2_2x/Material.c b/source/blender/python/api2_2x/Material.c index 74c93c47604..ae8eba049bb 100644 --- a/source/blender/python/api2_2x/Material.c +++ b/source/blender/python/api2_2x/Material.c @@ -41,6 +41,7 @@ #include "BKE_library.h" #include "BKE_material.h" #include "BKE_texture.h" +#include "BKE_idprop.h" #include "MEM_guardedalloc.h" #include "BLI_blenlib.h" #include "BSE_editipo.h" @@ -52,6 +53,7 @@ #include "Ipo.h" #include "Group.h" #include "gen_utils.h" +#include "IDProp.h" /*****************************************************************************/ /* Python BPy_Material defaults: */ @@ -602,6 +604,7 @@ static PyObject *Material_clearScriptLinks(BPy_Material *self, PyObject *args); static PyObject *Material_insertIpoKey( BPy_Material * self, PyObject * args ); static PyObject *Material_copy( BPy_Material * self ); +static PyObject *Material_getProperties( BPy_Material * self ); /*****************************************************************************/ @@ -609,6 +612,8 @@ static PyObject *Material_copy( BPy_Material * self ); /*****************************************************************************/ static PyMethodDef BPy_Material_methods[] = { /* name, method, flags, doc */ + {"getProperties", ( PyCFunction) Material_getProperties, METH_NOARGS, + "() Return Material's ID Properties"}, {"getName", ( PyCFunction ) Material_getName, METH_NOARGS, "() - Return Material's name"}, {"getIpo", ( PyCFunction ) Material_getIpo, METH_NOARGS, @@ -1080,6 +1085,8 @@ static PyGetSetDef BPy_Material_getseters[] = { (getter)Material_getUsers, (setter)NULL, "Number of material users", NULL}, + {"properties", (getter) Material_getProperties, (setter)NULL, + "Get material's ID properties"}, {NULL,NULL,NULL,NULL,NULL} /* Sentinel */ }; @@ -1280,6 +1287,12 @@ Material *GetMaterialByName( char *name ) /* Python BPy_Material methods: */ /*****************************************************************************/ +static PyObject *Material_getProperties( BPy_Material * self ) +{ + /*sanity check, we set parent property type to Group here*/ + return BPy_Wrap_IDProperty((ID*)self->material, IDP_GetProperties((ID*)self->material, 1)); +} + static PyObject *Material_getIpo( BPy_Material * self ) { Ipo *ipo = self->material->ipo; diff --git a/source/blender/python/api2_2x/Object.c b/source/blender/python/api2_2x/Object.c index 9ec0ffa8c00..b93cd3924b8 100644 --- a/source/blender/python/api2_2x/Object.c +++ b/source/blender/python/api2_2x/Object.c @@ -66,6 +66,7 @@ struct rctf; #include "BKE_scene.h" #include "BKE_nla.h" #include "BKE_material.h" +#include "BKE_idprop.h" #include "BSE_editipo.h" #include "BSE_edit.h" @@ -114,6 +115,7 @@ struct rctf; #include "gen_utils.h" #include "EXPP_interface.h" #include "BIF_editkey.h" +#include "IDProp.h" /* Defines for insertIpoKey */ @@ -326,6 +328,7 @@ struct PyMethodDef M_Object_methods[] = { static int setupSB(Object* ob); /*Make sure Softbody Pointer is initialized */ static int setupPI(Object* ob); +static PyObject *Object_GetProperties(BPy_Object * self); static PyObject *Object_buildParts( BPy_Object * self ); static PyObject *Object_clearIpo( BPy_Object * self ); static PyObject *Object_clrParent( BPy_Object * self, PyObject * args ); @@ -750,6 +753,8 @@ works only if self and the object specified are of the same type."}, "() - Insert a Shape Key in the current object"}, {"__copy__", ( PyCFunction ) Object_copy, METH_NOARGS, "() - Return a copy of this object."}, + {"getProperties", ( PyCFunction ) Object_GetProperties, METH_NOARGS, + "() return a reference to the ID properties associated with this object."}, {NULL, NULL, 0, NULL} }; @@ -982,6 +987,12 @@ static PyObject *M_Object_Duplicate( PyObject * self_unused, /* Python BPy_Object methods: */ /*****************************************************************************/ +static PyObject *Object_GetProperties(BPy_Object * self) +{ + return BPy_Wrap_IDProperty((ID*)self->object, IDP_GetProperties((ID*)self->object, 1)); + +} + static PyObject *Object_buildParts( BPy_Object * self ) { build_particle_system( self->object ); @@ -4983,7 +4994,8 @@ static PyGetSetDef BPy_Object_getseters[] = { (getter)Object_getType, (setter)NULL, "String describing Object type", NULL}, - + {"properties", (getter)Object_GetProperties, (setter)NULL, + "Get the ID properties associated with this object"}, {NULL,NULL,NULL,NULL,NULL} /* Sentinel */ }; diff --git a/source/blender/python/api2_2x/doc/IDProp.py b/source/blender/python/api2_2x/doc/IDProp.py new file mode 100644 index 00000000000..3a12e5ea67d --- /dev/null +++ b/source/blender/python/api2_2x/doc/IDProp.py @@ -0,0 +1,65 @@ +class IDProperty: + """ + The IDProperty wrapper type + =========================== + @ivar name: the name of the property + @ivar type: the property type (is read-only) + @ivar data: the property's data. + """ + +class IDGroup: + """ + The IDGroup wrapper type + ======================== + This type supports both iteration and the [] + operator to get child ID properties. + + You can also add new properties using the [] operator. + For example: + + group['a float!'] = 0.0 + group['an int!'] = 0 + group['a string!'] = "hi!" + group['an array!'] = [0, 0, 1.0, 0] #note that any floats in the list + #makes the whole list a float array. + group['a subgroup!] = {"float": 0.0, "an int": 1.0, "an array": [1, 2], \ + "another subgroup": {"a": 0.0, "str": "bleh"}} + + you also do del group['item'] + """ + + def newProperty(type, name, array_type="Float", val=""): + """ + This function creates a new child ID property in the group. + @type type: an int or a string + @param type: The ID property type. Can be: + "String" or Blender.IDPropTypes['String'] + "Int" or Blender.IDPropTypes['Int'] + "Float" or Blender.IDPropTypes['Float'] + "Array" or Blender.IDPropTypes['Array'] + "Group" or Blender.IDPropTypes['Group'] + """ + + def deleteProperty(prop): + """ + deletes a property, takes either a name or a reference + as an argument. + """ + +class IDArray: + """ + The IDArray wrapper type + ======================== + + @ivar type: returns the type of the array, can be either IDP_Int or IDP_Float + """ + + def __getitem__(self): + pass + + def __setitem__(self): + pass + + def __len__(self): + pass +
\ No newline at end of file |