=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.
This commit is contained in:
Joseph Eagar
2006-11-17 04:46:48 +00:00
parent 24f4440d05
commit 8768707610
11 changed files with 1794 additions and 9 deletions

View File

@@ -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 */

View File

@@ -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);
}
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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;
/**

View File

@@ -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; */
};

View File

@@ -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);

View File

@@ -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;

View File

@@ -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 */
};

View File

@@ -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