From a7b3047cefcbfae4d8b13e15026497fd5ae92730 Mon Sep 17 00:00:00 2001 From: Alexander Romanov Date: Thu, 13 Apr 2017 12:30:03 +0300 Subject: Datablock ID Properties The absence of datablock properties "will certainly be resolved soon as the need for them is becoming obvious" said the [[http://wiki.blender.org/index.php/Dev:Ref/Release_Notes/2.67/Python_Nodes|Python Nodes release notes]]. So this patch allows Python scripts to create ID Properties which reference datablocks. This functionality is implemented for `PointerProperty` and now such properties can be created with Python. In addition to the standard update callback, `PointerProperty` can have a `poll` callback (standard RNA) which is useful for search menus. For details see the test included in this patch. Original author: @artfunkel Alexander (Blend4Web Team) Reviewers: brecht, artfunkel, mont29, campbellbarton Reviewed By: mont29, campbellbarton Subscribers: jta, sergey, campbellbarton, wisaac, poseidon4o, mont29, homyachetser, Evgeny_Rodygin, AlexKowel, yurikovelenov, fjuhec, sharlybg, cardboard, duarteframos, blueprintrandom, a.romanov, BYOB, disnel, aditiapratama, bliblubli, dfelinto, lukastoenne Maniphest Tasks: T37754 Differential Revision: https://developer.blender.org/D113 --- source/blender/blenkernel/BKE_idprop.h | 13 +- source/blender/blenkernel/BKE_library.h | 2 +- source/blender/blenkernel/BKE_library_query.h | 2 +- source/blender/blenkernel/intern/idprop.c | 117 +++++++---- source/blender/blenkernel/intern/library_query.c | 119 +++++++++-- source/blender/blenkernel/intern/library_remap.c | 30 ++- source/blender/blenkernel/intern/node.c | 2 +- source/blender/blenloader/intern/readfile.c | 219 ++++++++++++++------- source/blender/editors/object/object_relations.c | 29 +++ source/blender/makesdna/DNA_ID.h | 2 - source/blender/makesrna/RNA_access.h | 2 + source/blender/makesrna/RNA_define.h | 1 + source/blender/makesrna/RNA_types.h | 2 + source/blender/makesrna/intern/rna_ID.c | 6 +- source/blender/makesrna/intern/rna_access.c | 76 +++++-- source/blender/makesrna/intern/rna_define.c | 13 ++ source/blender/makesrna/intern/rna_internal.h | 1 + .../blender/makesrna/intern/rna_internal_types.h | 1 + source/blender/makesrna/intern/rna_ui.c | 1 + source/blender/makesrna/intern/rna_userdef.c | 1 + source/blender/makesrna/intern/rna_wm.c | 1 + source/blender/python/generic/idprop_py_api.c | 21 ++ source/blender/python/intern/bpy_props.c | 133 ++++++++++--- source/blender/python/intern/bpy_props.h | 4 + source/blender/python/intern/bpy_rna.c | 44 ++++- source/blender/python/intern/bpy_rna.h | 1 + source/blender/windowmanager/intern/wm.c | 2 +- 27 files changed, 641 insertions(+), 204 deletions(-) (limited to 'source/blender') diff --git a/source/blender/blenkernel/BKE_idprop.h b/source/blender/blenkernel/BKE_idprop.h index 5b10d7ebc06..ab8728faedb 100644 --- a/source/blender/blenkernel/BKE_idprop.h +++ b/source/blender/blenkernel/BKE_idprop.h @@ -60,8 +60,6 @@ typedef union IDPropertyTemplate { IDProperty *IDP_NewIDPArray(const char *name) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); IDProperty *IDP_CopyIDPArray(const IDProperty *array) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); -void IDP_FreeIDPArray(IDProperty *prop); - /* shallow copies item */ void IDP_SetIndexArray(struct IDProperty *prop, int index, struct IDProperty *item) ATTR_NONNULL(); struct IDProperty *IDP_GetIndexArray(struct IDProperty *prop, int index) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); @@ -81,8 +79,8 @@ void IDP_ConcatString(struct IDProperty *str1, struct IDProperty *append) ATTR_N void IDP_FreeString(struct IDProperty *prop) ATTR_NONNULL(); /*-------- ID Type -------*/ -void IDP_LinkID(struct IDProperty *prop, ID *id); -void IDP_UnlinkID(struct IDProperty *prop); + +typedef void(*IDPWalkFunc)(void *userData, IDProperty *idp); /*-------- Group Functions -------*/ @@ -112,11 +110,12 @@ bool IDP_EqualsProperties(struct IDProperty *prop1, struct IDProperty *prop2) AT struct IDProperty *IDP_New(const char type, const IDPropertyTemplate *val, const char *name) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +void IDP_FreeProperty_ex(struct IDProperty *prop, const bool do_id_user); void IDP_FreeProperty(struct IDProperty *prop); void IDP_ClearProperty(IDProperty *prop); -void IDP_UnlinkProperty(struct IDProperty *prop); +void IDP_RelinkProperty(struct IDProperty *prop); #define IDP_Int(prop) ((prop)->data.val) #define IDP_Array(prop) ((prop)->data.pointer) @@ -134,11 +133,15 @@ void IDP_UnlinkProperty(struct IDProperty *prop); # define IDP_IDPArray(prop) _Generic((prop), \ IDProperty *: ((IDProperty *) (prop)->data.pointer), \ const IDProperty *: ((const IDProperty *) (prop)->data.pointer)) +# define IDP_Id(prop) _Generic((prop), \ + IDProperty *: ((ID *) (prop)->data.pointer), \ + const IDProperty *: ((const ID *) (prop)->data.pointer)) #else # define IDP_Float(prop) (*(float *)&(prop)->data.val) # define IDP_Double(prop) (*(double *)&(prop)->data.val) # define IDP_String(prop) ((char *) (prop)->data.pointer) # define IDP_IDPArray(prop) ((IDProperty *) (prop)->data.pointer) +# define IDP_Id(prop) ((ID *) (prop)->data.pointer) #endif #ifndef NDEBUG diff --git a/source/blender/blenkernel/BKE_library.h b/source/blender/blenkernel/BKE_library.h index 72ae2cf4efa..6649cfbb585 100644 --- a/source/blender/blenkernel/BKE_library.h +++ b/source/blender/blenkernel/BKE_library.h @@ -66,7 +66,7 @@ struct ID *BKE_libblock_find_name(const short type, const char *name) ATTR_WARN_ void BKE_libblock_free(struct Main *bmain, void *idv) ATTR_NONNULL(); void BKE_libblock_free_ex(struct Main *bmain, void *idv, const bool do_id_user, const bool do_ui_user) ATTR_NONNULL(); void BKE_libblock_free_us(struct Main *bmain, void *idv) ATTR_NONNULL(); -void BKE_libblock_free_data(struct Main *bmain, struct ID *id) ATTR_NONNULL(); +void BKE_libblock_free_data(struct Main *bmain, struct ID *id, const bool do_id_user) ATTR_NONNULL(); void BKE_libblock_delete(struct Main *bmain, void *idv) ATTR_NONNULL(); void BKE_id_lib_local_paths(struct Main *bmain, struct Library *lib, struct ID *id); diff --git a/source/blender/blenkernel/BKE_library_query.h b/source/blender/blenkernel/BKE_library_query.h index 1258e2fa72e..68bf9f43fe1 100644 --- a/source/blender/blenkernel/BKE_library_query.h +++ b/source/blender/blenkernel/BKE_library_query.h @@ -87,7 +87,7 @@ void BKE_library_update_ID_link_user(struct ID *id_dst, struct ID *id_src, const int BKE_library_ID_use_ID(struct ID *id_user, struct ID *id_used); -bool BKE_library_idtype_can_use_idtype(const short id_type_owner, const short id_type_used); +bool BKE_library_id_can_use_idtype(struct ID *id_owner, const short id_type_used); bool BKE_library_ID_is_locally_used(struct Main *bmain, void *idv); bool BKE_library_ID_is_indirectly_used(struct Main *bmain, void *idv); diff --git a/source/blender/blenkernel/intern/idprop.c b/source/blender/blenkernel/intern/idprop.c index 5e4e8eb34ad..074a9a12fe0 100644 --- a/source/blender/blenkernel/intern/idprop.c +++ b/source/blender/blenkernel/intern/idprop.c @@ -117,14 +117,14 @@ IDProperty *IDP_CopyIDPArray(const IDProperty *array) return narray; } -void IDP_FreeIDPArray(IDProperty *prop) +static void IDP_FreeIDPArray(IDProperty *prop, const bool do_id_user) { int i; BLI_assert(prop->type == IDP_IDPARRAY); for (i = 0; i < prop->len; i++) - IDP_FreeProperty(GETPROP(prop, i)); + IDP_FreeProperty_ex(GETPROP(prop, i), do_id_user); if (prop->data.pointer) MEM_freeN(prop->data.pointer); @@ -437,22 +437,24 @@ void IDP_FreeString(IDProperty *prop) /* -------------------------------------------------------------------- */ -/* ID Type (not in use yet) */ +/* ID Type */ -/** \name IDProperty ID API (unused) +/** \name IDProperty ID API * \{ */ -void IDP_LinkID(IDProperty *prop, ID *id) -{ - if (prop->data.pointer) - id_us_min(((ID *)prop->data.pointer)); - prop->data.pointer = id; - id_us_plus(id); -} -void IDP_UnlinkID(IDProperty *prop) +static IDProperty *IDP_CopyID(const IDProperty *prop) { - id_us_min(((ID *)prop->data.pointer)); + IDProperty *newp; + + BLI_assert(prop->type == IDP_ID); + newp = idp_generic_copy(prop); + + newp->data.pointer = prop->data.pointer; + id_us_plus(IDP_Id(newp)); + + return newp; } + /** \} */ @@ -710,13 +712,13 @@ IDProperty *IDP_GetPropertyTypeFromGroup(IDProperty *prop, const char *name, con * This is because all ID Property freeing functions free only direct data (not the ID Property * struct itself), but for Groups the child properties *are* considered * direct data. */ -static void IDP_FreeGroup(IDProperty *prop) +static void IDP_FreeGroup(IDProperty *prop, const bool do_id_user) { IDProperty *loop; BLI_assert(prop->type == IDP_GROUP); for (loop = prop->data.group.first; loop; loop = loop->next) { - IDP_FreeProperty(loop); + IDP_FreeProperty_ex(loop, do_id_user); } BLI_freelistN(&prop->data.group); } @@ -733,12 +735,51 @@ IDProperty *IDP_CopyProperty(const IDProperty *prop) switch (prop->type) { case IDP_GROUP: return IDP_CopyGroup(prop); case IDP_STRING: return IDP_CopyString(prop); + case IDP_ID: return IDP_CopyID(prop); case IDP_ARRAY: return IDP_CopyArray(prop); case IDP_IDPARRAY: return IDP_CopyIDPArray(prop); default: return idp_generic_copy(prop); } } +/* Updates ID pointers after an object has been copied */ +/* TODO Nuke this once its only user has been correctly converted to use generic ID management from BKE_library! */ +void IDP_RelinkProperty(struct IDProperty *prop) +{ + if (!prop) + return; + + switch (prop->type) { + case IDP_GROUP: + { + for (IDProperty *loop = prop->data.group.first; loop; loop = loop->next) { + IDP_RelinkProperty(loop); + } + break; + } + case IDP_IDPARRAY: + { + IDProperty *idp_array = IDP_Array(prop); + for (int i = 0; i < prop->len; i++) { + IDP_RelinkProperty(&idp_array[i]); + } + break; + } + case IDP_ID: + { + ID *id = IDP_Id(prop); + if (id && id->newid) { + id_us_min(IDP_Id(prop)); + prop->data.pointer = id->newid; + id_us_plus(IDP_Id(prop)); + } + break; + } + default: + break; /* Nothing to do for other IDProp types. */ + } +} + /** * 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 @@ -835,6 +876,8 @@ bool IDP_EqualsProperties_ex(IDProperty *prop1, IDProperty *prop2, const bool is return false; return true; } + case IDP_ID: + return (IDP_Id(prop1) == IDP_Id(prop2)); default: /* should never get here */ BLI_assert(0); @@ -910,6 +953,7 @@ IDProperty *IDP_New(const char type, const IDPropertyTemplate *val, const char * prop->len = prop->totallen = val->array.len; break; } + printf("%s: bad array type.\n",__func__); return NULL; } case IDP_STRING: @@ -956,6 +1000,14 @@ IDProperty *IDP_New(const char type, const IDPropertyTemplate *val, const char * /* heh I think all needed values are set properly by calloc anyway :) */ break; } + case IDP_ID: + { + prop = MEM_callocN(sizeof(IDProperty), "IDProperty datablock"); + prop->data.pointer = (void *)val->id; + prop->type = IDP_ID; + id_us_plus(IDP_Id(prop)); + break; + } default: { prop = MEM_callocN(sizeof(IDProperty), "IDProperty array"); @@ -970,11 +1022,10 @@ IDProperty *IDP_New(const char type, const IDPropertyTemplate *val, const char * } /** - * \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. + * \note This will free allocated data, all child properties of arrays and groups, and unlink IDs! + * But it does not free the actual IDProperty struct itself. */ -void IDP_FreeProperty(IDProperty *prop) +void IDP_FreeProperty_ex(IDProperty *prop, const bool do_id_user) { switch (prop->type) { case IDP_ARRAY: @@ -984,14 +1035,24 @@ void IDP_FreeProperty(IDProperty *prop) IDP_FreeString(prop); break; case IDP_GROUP: - IDP_FreeGroup(prop); + IDP_FreeGroup(prop, do_id_user); break; case IDP_IDPARRAY: - IDP_FreeIDPArray(prop); + IDP_FreeIDPArray(prop, do_id_user); + break; + case IDP_ID: + if (do_id_user) { + id_us_min(IDP_Id(prop)); + } break; } } +void IDP_FreeProperty(IDProperty *prop) +{ + IDP_FreeProperty_ex(prop, true); +} + void IDP_ClearProperty(IDProperty *prop) { IDP_FreeProperty(prop); @@ -999,18 +1060,4 @@ void IDP_ClearProperty(IDProperty *prop) prop->len = prop->totallen = 0; } -/** - * Unlinks any struct IDProperty<->ID linkage that might be going on. - * - * \note currently unused - */ -void IDP_UnlinkProperty(IDProperty *prop) -{ - switch (prop->type) { - case IDP_ID: - IDP_UnlinkID(prop); - break; - } -} - /** \} */ diff --git a/source/blender/blenkernel/intern/library_query.c b/source/blender/blenkernel/intern/library_query.c index 9685f1f5af6..0616c614848 100644 --- a/source/blender/blenkernel/intern/library_query.c +++ b/source/blender/blenkernel/intern/library_query.c @@ -33,6 +33,7 @@ #include "DNA_actuator_types.h" #include "DNA_anim_types.h" +#include "DNA_armature_types.h" #include "DNA_brush_types.h" #include "DNA_camera_types.h" #include "DNA_constraint_types.h" @@ -70,10 +71,12 @@ #include "BKE_animsys.h" #include "BKE_constraint.h" #include "BKE_fcurve.h" +#include "BKE_idprop.h" #include "BKE_library.h" #include "BKE_library_query.h" #include "BKE_main.h" #include "BKE_modifier.h" +#include "BKE_node.h" #include "BKE_particle.h" #include "BKE_rigidbody.h" #include "BKE_sca.h" @@ -82,7 +85,9 @@ #define FOREACH_FINALIZE _finalize -#define FOREACH_FINALIZE_VOID FOREACH_FINALIZE: (void)0 +#define FOREACH_FINALIZE_VOID \ + if (0) { goto FOREACH_FINALIZE; } \ + FOREACH_FINALIZE: ((void)0) #define FOREACH_CALLBACK_INVOKE_ID_PP(_data, id_pp, _cb_flag) \ CHECK_TYPE(id_pp, ID **); \ @@ -140,6 +145,37 @@ typedef struct LibraryForeachIDData { BLI_LINKSTACK_DECLARE(ids_todo, ID *); } LibraryForeachIDData; +static void library_foreach_idproperty_ID_link(LibraryForeachIDData *data, IDProperty *prop, int flag) +{ + if (!prop) + return; + + switch (prop->type) { + case IDP_GROUP: + { + for (IDProperty *loop = prop->data.group.first; loop; loop = loop->next) { + library_foreach_idproperty_ID_link(data, loop, flag); + } + break; + } + case IDP_IDPARRAY: + { + IDProperty *loop = IDP_Array(prop); + for (int i = 0; i < prop->len; i++) { + library_foreach_idproperty_ID_link(data, &loop[i], flag); + } + break; + } + case IDP_ID: + FOREACH_CALLBACK_INVOKE_ID(data, prop->data.pointer, flag); + break; + default: + break; /* Nothing to do here with other types of IDProperties... */ + } + + FOREACH_FINALIZE_VOID; +} + static void library_foreach_rigidbodyworldSceneLooper( struct RigidBodyWorld *UNUSED(rbw), ID **id_pointer, void *user_data, int cb_flag) { @@ -265,6 +301,17 @@ static void library_foreach_paint(LibraryForeachIDData *data, Paint *paint) FOREACH_FINALIZE_VOID; } +static void library_foreach_bone(LibraryForeachIDData *data, Bone *bone) +{ + library_foreach_idproperty_ID_link(data, bone->prop, IDWALK_CB_USER); + + for (Bone *curbone = bone->childbase.first; curbone; curbone = curbone->next) { + library_foreach_bone(data, curbone); + } + + FOREACH_FINALIZE_VOID; +} + static void library_foreach_ID_as_subdata_link( ID **id_pp, LibraryIDLinkCallback callback, void *user_data, int flag, LibraryForeachIDData *data) { @@ -337,6 +384,8 @@ void BKE_library_foreach_ID_link(Main *bmain, ID *id, LibraryIDLinkCallback call continue; } + library_foreach_idproperty_ID_link(&data, id->properties, IDWALK_CB_USER); + AnimData *adt = BKE_animdata_from_id(id); if (adt) { library_foreach_animationData(&data, adt); @@ -402,6 +451,7 @@ void BKE_library_foreach_ID_link(Main *bmain, ID *id, LibraryIDLinkCallback call CALLBACK_INVOKE(seq->clip, IDWALK_CB_USER); CALLBACK_INVOKE(seq->mask, IDWALK_CB_USER); CALLBACK_INVOKE(seq->sound, IDWALK_CB_USER); + library_foreach_idproperty_ID_link(&data, seq->prop, IDWALK_CB_USER); for (SequenceModifierData *smd = seq->modifiers.first; smd; smd = smd->next) { CALLBACK_INVOKE(smd->mask_id, IDWALK_CB_USER); } @@ -515,6 +565,7 @@ void BKE_library_foreach_ID_link(Main *bmain, ID *id, LibraryIDLinkCallback call data.cb_flag |= proxy_cb_flag; for (pchan = object->pose->chanbase.first; pchan; pchan = pchan->next) { + library_foreach_idproperty_ID_link(&data, pchan->prop, IDWALK_CB_USER); CALLBACK_INVOKE(pchan->custom, IDWALK_CB_USER); BKE_constraints_id_loop(&pchan->constraints, library_foreach_constraintObjectLooper, &data); } @@ -554,6 +605,16 @@ void BKE_library_foreach_ID_link(Main *bmain, ID *id, LibraryIDLinkCallback call break; } + case ID_AR: + { + bArmature *arm = (bArmature *)id; + + for (Bone *bone = arm->bonebase.first; bone; bone = bone->next) { + library_foreach_bone(&data, bone); + } + break; + } + case ID_ME: { Mesh *mesh = (Mesh *) id; @@ -736,9 +797,27 @@ void BKE_library_foreach_ID_link(Main *bmain, ID *id, LibraryIDLinkCallback call { bNodeTree *ntree = (bNodeTree *) id; bNode *node; + bNodeSocket *sock; + CALLBACK_INVOKE(ntree->gpd, IDWALK_CB_USER); + for (node = ntree->nodes.first; node; node = node->next) { CALLBACK_INVOKE_ID(node->id, IDWALK_CB_USER); + + library_foreach_idproperty_ID_link(&data, node->prop, IDWALK_CB_USER); + for (sock = node->inputs.first; sock; sock = sock->next) { + library_foreach_idproperty_ID_link(&data, sock->prop, IDWALK_CB_USER); + } + for (sock = node->outputs.first; sock; sock = sock->next) { + library_foreach_idproperty_ID_link(&data, sock->prop, IDWALK_CB_USER); + } + } + + for (sock = ntree->inputs.first; sock; sock = sock->next) { + library_foreach_idproperty_ID_link(&data, sock->prop, IDWALK_CB_USER); + } + for (sock = ntree->outputs.first; sock; sock = sock->next) { + library_foreach_idproperty_ID_link(&data, sock->prop, IDWALK_CB_USER); } break; } @@ -898,7 +977,6 @@ void BKE_library_foreach_ID_link(Main *bmain, ID *id, LibraryIDLinkCallback call case ID_VF: case ID_TXT: case ID_SO: - case ID_AR: case ID_GD: case ID_WM: case ID_PAL: @@ -948,9 +1026,25 @@ void BKE_library_update_ID_link_user(ID *id_dst, ID *id_src, const int cb_flag) */ /* XXX This has to be fully rethink, basing check on ID type is not really working anymore (and even worth once * IDProps will support ID pointers), we'll have to do some quick checks on IDs themselves... */ -bool BKE_library_idtype_can_use_idtype(const short id_type_owner, const short id_type_used) +bool BKE_library_id_can_use_idtype(ID *id_owner, const short id_type_used) { - if (id_type_can_have_animdata(id_type_owner)) { + /* any type of ID can be used in custom props. */ + if (id_owner->properties) { + return true; + } + + const short id_type_owner = GS(id_owner->name); + + /* IDProps of armature bones and nodes, and bNode->id can use virtually any type of ID. */ + if (ELEM(id_type_owner, ID_NT, ID_AR)) { + return true; + } + + if (ntreeFromID(id_owner)) { + return true; + } + + if (BKE_animdata_from_id(id_owner)) { return true; /* AnimationData can use virtually any kind of datablocks, through drivers especially. */ } @@ -959,8 +1053,7 @@ bool BKE_library_idtype_can_use_idtype(const short id_type_owner, const short id return ELEM(id_type_used, ID_LI); case ID_SCE: return (ELEM(id_type_used, ID_OB, ID_WO, ID_SCE, ID_MC, ID_MA, ID_GR, ID_TXT, - ID_LS, ID_MSK, ID_SO, ID_GD, ID_BR, ID_PAL, ID_IM, ID_NT) || - BKE_library_idtype_can_use_idtype(ID_NT, id_type_used)); + ID_LS, ID_MSK, ID_SO, ID_GD, ID_BR, ID_PAL, ID_IM, ID_NT)); case ID_OB: /* Could be the following, but simpler to just always say 'yes' here. */ #if 0 @@ -977,13 +1070,13 @@ bool BKE_library_idtype_can_use_idtype(const short id_type_owner, const short id case ID_MB: return ELEM(id_type_used, ID_MA); case ID_MA: - return (ELEM(id_type_used, ID_TE, ID_GR) || BKE_library_idtype_can_use_idtype(ID_NT, id_type_used)); + return (ELEM(id_type_used, ID_TE, ID_GR)); case ID_TE: - return (ELEM(id_type_used, ID_IM, ID_OB) || BKE_library_idtype_can_use_idtype(ID_NT, id_type_used)); + return (ELEM(id_type_used, ID_IM, ID_OB)); case ID_LT: return ELEM(id_type_used, ID_KE); case ID_LA: - return (ELEM(id_type_used, ID_TE) || BKE_library_idtype_can_use_idtype(ID_NT, id_type_used)); + return (ELEM(id_type_used, ID_TE)); case ID_CA: return ELEM(id_type_used, ID_OB); case ID_KE: @@ -991,7 +1084,7 @@ bool BKE_library_idtype_can_use_idtype(const short id_type_owner, const short id case ID_SCR: return ELEM(id_type_used, ID_SCE); case ID_WO: - return (ELEM(id_type_used, ID_TE) || BKE_library_idtype_can_use_idtype(ID_NT, id_type_used)); + return (ELEM(id_type_used, ID_TE)); case ID_SPK: return ELEM(id_type_used, ID_SO); case ID_GR: @@ -1012,7 +1105,7 @@ bool BKE_library_idtype_can_use_idtype(const short id_type_owner, const short id case ID_MSK: return ELEM(id_type_used, ID_MC); /* WARNING! mask->parent.id, not typed. */ case ID_LS: - return (ELEM(id_type_used, ID_TE, ID_OB) || BKE_library_idtype_can_use_idtype(ID_NT, id_type_used)); + return (ELEM(id_type_used, ID_TE, ID_OB)); case ID_IM: case ID_VF: case ID_TXT: @@ -1118,7 +1211,7 @@ static bool library_ID_is_used(Main *bmain, void *idv, const bool check_linked) while (i-- && !is_defined) { ID *id_curr = lb_array[i]->first; - if (!id_curr || !BKE_library_idtype_can_use_idtype(GS(id_curr->name), GS(id->name))) { + if (!id_curr || !BKE_library_id_can_use_idtype(id_curr, GS(id->name))) { continue; } @@ -1170,7 +1263,7 @@ void BKE_library_ID_test_usages(Main *bmain, void *idv, bool *is_used_local, boo while (i-- && !is_defined) { ID *id_curr = lb_array[i]->first; - if (!id_curr || !BKE_library_idtype_can_use_idtype(GS(id_curr->name), GS(id->name))) { + if (!id_curr || !BKE_library_id_can_use_idtype(id_curr, GS(id->name))) { continue; } diff --git a/source/blender/blenkernel/intern/library_remap.c b/source/blender/blenkernel/intern/library_remap.c index b6f4621a0b3..d14e0cf0b65 100644 --- a/source/blender/blenkernel/intern/library_remap.c +++ b/source/blender/blenkernel/intern/library_remap.c @@ -448,20 +448,16 @@ ATTR_NONNULL(1) static void libblock_remap_data( * objects actually using given old_id... sounds rather unlikely currently, though, so this will do for now. */ while (i--) { - ID *id_curr = lb_array[i]->first; - - if (!id_curr || !BKE_library_idtype_can_use_idtype(GS(id_curr->name), GS(old_id->name))) { - continue; - } - - for (; id_curr; id_curr = id_curr->next) { - /* Note that we cannot skip indirect usages of old_id here (if requested), we still need to check it for - * the user count handling... - * XXX No more true (except for debug usage of those skipping counters). */ - r_id_remap_data->id = id_curr; - libblock_remap_data_preprocess(r_id_remap_data); - BKE_library_foreach_ID_link( - NULL, id_curr, foreach_libblock_remap_callback, (void *)r_id_remap_data, IDWALK_NOP); + for (ID *id_curr = lb_array[i]->first; id_curr; id_curr = id_curr->next) { + if (BKE_library_id_can_use_idtype(id_curr, GS(old_id->name))) { + /* Note that we cannot skip indirect usages of old_id here (if requested), we still need to check it for + * the user count handling... + * XXX No more true (except for debug usage of those skipping counters). */ + r_id_remap_data->id = id_curr; + libblock_remap_data_preprocess(r_id_remap_data); + BKE_library_foreach_ID_link( + NULL, id_curr, foreach_libblock_remap_callback, (void *)r_id_remap_data, IDWALK_NOP); + } } } } @@ -723,10 +719,10 @@ void BKE_libblock_relink_to_newid(ID *id) BKE_library_foreach_ID_link(NULL, id, id_relink_to_newid_looper, NULL, 0); } -void BKE_libblock_free_data(Main *UNUSED(bmain), ID *id) +void BKE_libblock_free_data(Main *UNUSED(bmain), ID *id, const bool do_id_user) { if (id->properties) { - IDP_FreeProperty(id->properties); + IDP_FreeProperty_ex(id->properties, do_id_user); MEM_freeN(id->properties); } } @@ -876,7 +872,7 @@ void BKE_libblock_free_ex(Main *bmain, void *idv, const bool do_id_user, const b BLI_remlink(lb, id); - BKE_libblock_free_data(bmain, id); + BKE_libblock_free_data(bmain, id, do_id_user); BKE_main_unlock(bmain); MEM_freeN(id); diff --git a/source/blender/blenkernel/intern/node.c b/source/blender/blenkernel/intern/node.c index 3f3b4896653..f3223e31b17 100644 --- a/source/blender/blenkernel/intern/node.c +++ b/source/blender/blenkernel/intern/node.c @@ -1828,7 +1828,7 @@ void ntreeFreeTree(bNodeTree *ntree) if (tntree == ntree) break; if (tntree == NULL) { - BKE_libblock_free_data(G.main, &ntree->id); + BKE_libblock_free_data(G.main, &ntree->id, true); } } diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 09c88ac945a..c55b426c025 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -129,6 +129,7 @@ #include "BKE_library_idmap.h" #include "BKE_library_query.h" #include "BKE_idcode.h" +#include "BKE_idprop.h" #include "BKE_material.h" #include "BKE_main.h" // for Main #include "BKE_mesh.h" // for ME_ defines (patching) @@ -2001,7 +2002,7 @@ static void test_pointer_array(FileData *fd, void **mat) /* ************ READ ID Properties *************** */ static void IDP_DirectLinkProperty(IDProperty *prop, int switch_endian, FileData *fd); -static void IDP_LibLinkProperty(IDProperty *prop, int switch_endian, FileData *fd); +static void IDP_LibLinkProperty(IDProperty *prop, FileData *fd); static void IDP_DirectLinkIDPArray(IDProperty *prop, int switch_endian, FileData *fd) { @@ -2132,10 +2133,39 @@ static void _IDP_DirectLinkGroup_OrFree(IDProperty **prop, int switch_endian, Fi } } -/* stub function */ -static void IDP_LibLinkProperty(IDProperty *UNUSED(prop), int UNUSED(switch_endian), FileData *UNUSED(fd)) +static void IDP_LibLinkProperty(IDProperty *prop, FileData *fd) { - /* Should we do something here, prop should be ensured to be non-NULL first... */ + if (!prop) + return; + + switch (prop->type) { + case IDP_ID: /* PointerProperty */ + { + void *newaddr = newlibadr_us(fd, NULL, IDP_Id(prop)); + if (IDP_Id(prop) && !newaddr && G.debug) { + printf("Error while loading \"%s\". Data not found in file!\n", prop->name); + } + prop->data.pointer = newaddr; + break; + } + case IDP_IDPARRAY: /* CollectionProperty */ + { + IDProperty *idp_array = IDP_IDPArray(prop); + for (int i = 0; i < prop->len; i++) { + IDP_LibLinkProperty(&(idp_array[i]), fd); + } + break; + } + case IDP_GROUP: /* PointerProperty */ + { + for (IDProperty *loop = prop->data.group.first; loop; loop = loop->next) { + IDP_LibLinkProperty(loop, fd); + } + break; + } + default: + break; /* Nothing to do for other IDProps. */ + } } /* ************ READ IMAGE PREVIEW *************** */ @@ -2195,7 +2225,7 @@ static void lib_link_brush(FileData *fd, Main *main) /* only link ID pointers */ for (Brush *brush = main->brush.first; brush; brush = brush->id.next) { if (brush->id.tag & LIB_TAG_NEED_LINK) { - IDP_LibLinkProperty(brush->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd); + IDP_LibLinkProperty(brush->id.properties, fd); /* brush->(mask_)mtex.obj is ignored on purpose? */ brush->mtex.tex = newlibadr_us(fd, brush->id.lib, brush->mtex.tex); @@ -2232,7 +2262,7 @@ static void lib_link_palette(FileData *fd, Main *main) /* only link ID pointers */ for (Palette *palette = main->palettes.first; palette; palette = palette->id.next) { if (palette->id.tag & LIB_TAG_NEED_LINK) { - IDP_LibLinkProperty(palette->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd); + IDP_LibLinkProperty(palette->id.properties, fd); palette->id.tag &= ~LIB_TAG_NEED_LINK; } @@ -2250,7 +2280,7 @@ static void lib_link_paint_curve(FileData *fd, Main *main) /* only link ID pointers */ for (PaintCurve *pc = main->paintcurves.first; pc; pc = pc->id.next) { if (pc->id.tag & LIB_TAG_NEED_LINK) { - IDP_LibLinkProperty(pc->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd); + IDP_LibLinkProperty(pc->id.properties, fd); pc->id.tag &= ~LIB_TAG_NEED_LINK; } @@ -2505,7 +2535,7 @@ static void lib_link_action(FileData *fd, Main *main) { for (bAction *act = main->action.first; act; act = act->id.next) { if (act->id.tag & LIB_TAG_NEED_LINK) { - IDP_LibLinkProperty(act->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd); + IDP_LibLinkProperty(act->id.properties, fd); // XXX deprecated - old animation system <<< for (bActionChannel *chan = act->chanbase.first; chan; chan = chan->next) { @@ -2712,7 +2742,7 @@ static void lib_link_cachefiles(FileData *fd, Main *bmain) /* only link ID pointers */ for (CacheFile *cache_file = bmain->cachefiles.first; cache_file; cache_file = cache_file->id.next) { if (cache_file->id.tag & LIB_TAG_NEED_LINK) { - IDP_LibLinkProperty(cache_file->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd); + IDP_LibLinkProperty(cache_file->id.properties, fd); lib_link_animdata(fd, &cache_file->id, cache_file->adt); cache_file->id.tag &= ~LIB_TAG_NEED_LINK; @@ -2746,20 +2776,13 @@ static void direct_link_motionpath(FileData *fd, bMotionPath *mpath) /* ************ READ NODE TREE *************** */ -static void lib_link_node_socket(FileData *fd, ID *UNUSED(id), bNodeSocket *sock) -{ - /* Link ID Properties -- and copy this comment EXACTLY for easy finding - * of library blocks that implement this.*/ - IDP_LibLinkProperty(sock->prop, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd); -} - /* Single node tree (also used for material/scene trees), ntree is not NULL */ static void lib_link_ntree(FileData *fd, ID *id, bNodeTree *ntree) { bNode *node; bNodeSocket *sock; - IDP_LibLinkProperty(ntree->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd); + IDP_LibLinkProperty(ntree->id.properties, fd); lib_link_animdata(fd, &ntree->id, ntree->adt); ntree->gpd = newlibadr_us(fd, id->lib, ntree->gpd); @@ -2767,27 +2790,23 @@ static void lib_link_ntree(FileData *fd, ID *id, bNodeTree *ntree) for (node = ntree->nodes.first; node; node = node->next) { /* Link ID Properties -- and copy this comment EXACTLY for easy finding * of library blocks that implement this.*/ - IDP_LibLinkProperty(node->prop, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd); + IDP_LibLinkProperty(node->prop, fd); node->id = newlibadr_us(fd, id->lib, node->id); for (sock = node->inputs.first; sock; sock = sock->next) { - IDP_LibLinkProperty(sock->prop, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd); - lib_link_node_socket(fd, id, sock); + IDP_LibLinkProperty(sock->prop, fd); } for (sock = node->outputs.first; sock; sock = sock->next) { - IDP_LibLinkProperty(sock->prop, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd); - lib_link_node_socket(fd, id, sock); + IDP_LibLinkProperty(sock->prop, fd); } } for (sock = ntree->inputs.first; sock; sock = sock->next) { - IDP_LibLinkProperty(sock->prop, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd); - lib_link_node_socket(fd, id, sock); + IDP_LibLinkProperty(sock->prop, fd); } for (sock = ntree->outputs.first; sock; sock = sock->next) { - IDP_LibLinkProperty(sock->prop, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd); - lib_link_node_socket(fd, id, sock); + IDP_LibLinkProperty(sock->prop, fd); } } @@ -3292,6 +3311,8 @@ static void lib_link_pose(FileData *fd, Main *bmain, Object *ob, bPose *pose) pchan->bone = BLI_ghash_lookup(bone_hash, pchan->name); + IDP_LibLinkProperty(pchan->prop, fd); + pchan->custom = newlibadr_us(fd, arm->id.lib, pchan->custom); if (UNLIKELY(pchan->bone == NULL)) { rebuild = true; @@ -3312,13 +3333,26 @@ static void lib_link_pose(FileData *fd, Main *bmain, Object *ob, bPose *pose) } } +static void lib_link_bones(FileData *fd, Bone *bone) +{ + IDP_LibLinkProperty(bone->prop, fd); + + for (Bone *curbone = bone->childbase.first; curbone; curbone = curbone->next) { + lib_link_bones(fd, curbone); + } +} + static void lib_link_armature(FileData *fd, Main *main) { for (bArmature *arm = main->armature.first; arm; arm = arm->id.next) { if (arm->id.tag & LIB_TAG_NEED_LINK) { - IDP_LibLinkProperty(arm->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd); + IDP_LibLinkProperty(arm->id.properties, fd); lib_link_animdata(fd, &arm->id, arm->adt); + for (Bone *curbone = arm->bonebase.first; curbone; curbone = curbone->next) { + lib_link_bones(fd, curbone); + } + arm->id.tag &= ~LIB_TAG_NEED_LINK; } } @@ -3365,7 +3399,7 @@ static void lib_link_camera(FileData *fd, Main *main) { for (Camera *ca = main->camera.first; ca; ca = ca->id.next) { if (ca->id.tag & LIB_TAG_NEED_LINK) { - IDP_LibLinkProperty(ca->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd); + IDP_LibLinkProperty(ca->id.properties, fd); lib_link_animdata(fd, &ca->id, ca->adt); ca->ipo = newlibadr_us(fd, ca->id.lib, ca->ipo); // XXX deprecated - old animation system @@ -3390,7 +3424,7 @@ static void lib_link_lamp(FileData *fd, Main *main) { for (Lamp *la = main->lamp.first; la; la = la->id.next) { if (la->id.tag & LIB_TAG_NEED_LINK) { - IDP_LibLinkProperty(la->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd); + IDP_LibLinkProperty(la->id.properties, fd); lib_link_animdata(fd, &la->id, la->adt); for (int a = 0; a < MAX_MTEX; a++) { @@ -3455,7 +3489,7 @@ static void lib_link_key(FileData *fd, Main *main) BLI_assert((key->id.tag & LIB_TAG_EXTERN) == 0); if (key->id.tag & LIB_TAG_NEED_LINK) { - IDP_LibLinkProperty(key->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd); + IDP_LibLinkProperty(key->id.properties, fd); lib_link_animdata(fd, &key->id, key->adt); key->ipo = newlibadr_us(fd, key->id.lib, key->ipo); // XXX deprecated - old animation system @@ -3520,7 +3554,7 @@ static void lib_link_mball(FileData *fd, Main *main) { for (MetaBall *mb = main->mball.first; mb; mb = mb->id.next) { if (mb->id.tag & LIB_TAG_NEED_LINK) { - IDP_LibLinkProperty(mb->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd); + IDP_LibLinkProperty(mb->id.properties, fd); lib_link_animdata(fd, &mb->id, mb->adt); for (int a = 0; a < mb->totcol; a++) { @@ -3556,7 +3590,7 @@ static void lib_link_world(FileData *fd, Main *main) { for (World *wrld = main->world.first; wrld; wrld = wrld->id.next) { if (wrld->id.tag & LIB_TAG_NEED_LINK) { - IDP_LibLinkProperty(wrld->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd); + IDP_LibLinkProperty(wrld->id.properties, fd); lib_link_animdata(fd, &wrld->id, wrld->adt); wrld->ipo = newlibadr_us(fd, wrld->id.lib, wrld->ipo); // XXX deprecated - old animation system @@ -3607,7 +3641,7 @@ static void lib_link_vfont(FileData *fd, Main *main) { for (VFont *vf = main->vfont.first; vf; vf = vf->id.next) { if (vf->id.tag & LIB_TAG_NEED_LINK) { - IDP_LibLinkProperty(vf->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd); + IDP_LibLinkProperty(vf->id.properties, fd); vf->id.tag &= ~LIB_TAG_NEED_LINK; } @@ -3627,7 +3661,7 @@ static void lib_link_text(FileData *fd, Main *main) { for (Text *text = main->text.first; text; text = text->id.next) { if (text->id.tag & LIB_TAG_NEED_LINK) { - IDP_LibLinkProperty(text->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd); + IDP_LibLinkProperty(text->id.properties, fd); text->id.tag &= ~LIB_TAG_NEED_LINK; } @@ -3679,7 +3713,7 @@ static void lib_link_image(FileData *fd, Main *main) { for (Image *ima = main->image.first; ima; ima = ima->id.next) { if (ima->id.tag & LIB_TAG_NEED_LINK) { - IDP_LibLinkProperty(ima->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd); + IDP_LibLinkProperty(ima->id.properties, fd); ima->id.tag &= ~LIB_TAG_NEED_LINK; } @@ -3746,7 +3780,7 @@ static void lib_link_curve(FileData *fd, Main *main) { for (Curve *cu = main->curve.first; cu; cu = cu->id.next) { if (cu->id.tag & LIB_TAG_NEED_LINK) { - IDP_LibLinkProperty(cu->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd); + IDP_LibLinkProperty(cu->id.properties, fd); lib_link_animdata(fd, &cu->id, cu->adt); for (int a = 0; a < cu->totcol; a++) { @@ -3838,7 +3872,7 @@ static void lib_link_texture(FileData *fd, Main *main) { for (Tex *tex = main->tex.first; tex; tex = tex->id.next) { if (tex->id.tag & LIB_TAG_NEED_LINK) { - IDP_LibLinkProperty(tex->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd); + IDP_LibLinkProperty(tex->id.properties, fd); lib_link_animdata(fd, &tex->id, tex->adt); tex->ima = newlibadr_us(fd, tex->id.lib, tex->ima); @@ -3916,12 +3950,12 @@ static void lib_link_material(FileData *fd, Main *main) { for (Material *ma = main->mat.first; ma; ma = ma->id.next) { if (ma->id.tag & LIB_TAG_NEED_LINK) { - IDP_LibLinkProperty(ma->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd); + IDP_LibLinkProperty(ma->id.properties, fd); lib_link_animdata(fd, &ma->id, ma->adt); /* Link ID Properties -- and copy this comment EXACTLY for easy finding * of library blocks that implement this.*/ - IDP_LibLinkProperty(ma->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd); + IDP_LibLinkProperty(ma->id.properties, fd); ma->ipo = newlibadr_us(fd, ma->id.lib, ma->ipo); // XXX deprecated - old animation system ma->group = newlibadr_us(fd, ma->id.lib, ma->group); @@ -4060,7 +4094,7 @@ static void lib_link_particlesettings(FileData *fd, Main *main) { for (ParticleSettings *part = main->particle.first; part; part = part->id.next) { if (part->id.tag & LIB_TAG_NEED_LINK) { - IDP_LibLinkProperty(part->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd); + IDP_LibLinkProperty(part->id.properties, fd); lib_link_animdata(fd, &part->id, part->adt); part->ipo = newlibadr_us(fd, part->id.lib, part->ipo); // XXX deprecated - old animation system @@ -4382,7 +4416,7 @@ static void lib_link_mesh(FileData *fd, Main *main) /* Link ID Properties -- and copy this comment EXACTLY for easy finding * of library blocks that implement this.*/ - IDP_LibLinkProperty(me->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd); + IDP_LibLinkProperty(me->id.properties, fd); lib_link_animdata(fd, &me->id, me->adt); /* this check added for python created meshes */ @@ -4657,7 +4691,7 @@ static void lib_link_latt(FileData *fd, Main *main) { for (Lattice *lt = main->latt.first; lt; lt = lt->id.next) { if (lt->id.tag & LIB_TAG_NEED_LINK) { - IDP_LibLinkProperty(lt->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd); + IDP_LibLinkProperty(lt->id.properties, fd); lib_link_animdata(fd, <->id, lt->adt); lt->ipo = newlibadr_us(fd, lt->id.lib, lt->ipo); // XXX deprecated - old animation system @@ -4707,7 +4741,7 @@ static void lib_link_object(FileData *fd, Main *main) if (ob->id.tag & LIB_TAG_NEED_LINK) { int a; - IDP_LibLinkProperty(ob->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd); + IDP_LibLinkProperty(ob->id.properties, fd); lib_link_animdata(fd, &ob->id, ob->adt); // XXX deprecated - old animation system <<< @@ -5675,7 +5709,7 @@ static void lib_link_scene(FileData *fd, Main *main) if (sce->id.tag & LIB_TAG_NEED_LINK) { /* Link ID Properties -- and copy this comment EXACTLY for easy finding * of library blocks that implement this.*/ - IDP_LibLinkProperty(sce->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd); + IDP_LibLinkProperty(sce->id.properties, fd); lib_link_animdata(fd, &sce->id, sce->adt); lib_link_keyingsets(fd, &sce->id, &sce->keyingsets); @@ -5728,6 +5762,8 @@ static void lib_link_scene(FileData *fd, Main *main) Sequence *seq; SEQ_BEGIN (sce->ed, seq) { + IDP_LibLinkProperty(seq->prop, fd); + if (seq->ipo) seq->ipo = newlibadr_us(fd, sce->id.lib, seq->ipo); // XXX deprecated - old animation system seq->scene_sound = NULL; if (seq->scene) { @@ -6258,7 +6294,7 @@ static void lib_link_gpencil(FileData *fd, Main *main) { for (bGPdata *gpd = main->gpencil.first; gpd; gpd = gpd->id.next) { if (gpd->id.tag & LIB_TAG_NEED_LINK) { - IDP_LibLinkProperty(gpd->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd); + IDP_LibLinkProperty(gpd->id.properties, fd); lib_link_animdata(fd, &gpd->id, gpd->adt); gpd->id.tag &= ~LIB_TAG_NEED_LINK; @@ -6325,7 +6361,7 @@ static void lib_link_screen(FileData *fd, Main *main) { for (bScreen *sc = main->screen.first; sc; sc = sc->id.next) { if (sc->id.tag & LIB_TAG_NEED_LINK) { - IDP_LibLinkProperty(sc->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd); + IDP_LibLinkProperty(sc->id.properties, fd); id_us_ensure_real(&sc->id); sc->scene = newlibadr(fd, sc->id.lib, sc->scene); @@ -7411,7 +7447,7 @@ static void lib_link_speaker(FileData *fd, Main *main) { for (Speaker *spk = main->speaker.first; spk; spk = spk->id.next) { if (spk->id.tag & LIB_TAG_NEED_LINK) { - IDP_LibLinkProperty(spk->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd); + IDP_LibLinkProperty(spk->id.properties, fd); lib_link_animdata(fd, &spk->id, spk->adt); spk->sound = newlibadr_us(fd, spk->id.lib, spk->sound); @@ -7467,7 +7503,7 @@ static void lib_link_sound(FileData *fd, Main *main) { for (bSound *sound = main->sound.first; sound; sound = sound->id.next) { if (sound->id.tag & LIB_TAG_NEED_LINK) { - IDP_LibLinkProperty(sound->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd); + IDP_LibLinkProperty(sound->id.properties, fd); sound->ipo = newlibadr_us(fd, sound->id.lib, sound->ipo); // XXX deprecated - old animation system @@ -7490,7 +7526,7 @@ static void lib_link_group(FileData *fd, Main *main) { for (Group *group = main->group.first; group; group = group->id.next) { if (group->id.tag & LIB_TAG_NEED_LINK) { - IDP_LibLinkProperty(group->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd); + IDP_LibLinkProperty(group->id.properties, fd); bool add_us = false; @@ -7616,7 +7652,7 @@ static void lib_link_movieclip(FileData *fd, Main *main) if (clip->id.tag & LIB_TAG_NEED_LINK) { MovieTracking *tracking = &clip->tracking; - IDP_LibLinkProperty(clip->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd); + IDP_LibLinkProperty(clip->id.properties, fd); lib_link_animdata(fd, &clip->id, clip->adt); clip->gpd = newlibadr_us(fd, clip->id.lib, clip->gpd); @@ -7703,7 +7739,7 @@ static void lib_link_mask(FileData *fd, Main *main) { for (Mask *mask = main->mask.first; mask; mask = mask->id.next) { if (mask->id.tag & LIB_TAG_NEED_LINK) { - IDP_LibLinkProperty(mask->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd); + IDP_LibLinkProperty(mask->id.properties, fd); lib_link_animdata(fd, &mask->id, mask->adt); for (MaskLayer *masklay = mask->masklayers.first; masklay; masklay = masklay->next) { @@ -7738,7 +7774,7 @@ static void lib_link_linestyle(FileData *fd, Main *main) if (linestyle->id.tag & LIB_TAG_NEED_LINK) { LineStyleModifier *m; - IDP_LibLinkProperty(linestyle->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd); + IDP_LibLinkProperty(linestyle->id.properties, fd); lib_link_animdata(fd, &linestyle->id, linestyle->adt); for (m = linestyle->color_modifiers.first; m; m = m->next) { @@ -8905,6 +8941,31 @@ static void expand_constraint_channels(FileData *fd, Main *mainvar, ListBase *ch } } +static void expand_idprops(FileData *fd, Main *mainvar, IDProperty *prop) +{ + if (!prop) + return; + + switch (prop->type) { + case IDP_ID: + expand_doit(fd, mainvar, IDP_Id(prop)); + break; + case IDP_IDPARRAY: + { + IDProperty *idp_array = IDP_IDPArray(prop); + for (int i = 0; i < prop->len; i++) { + expand_idprops(fd, mainvar, &idp_array[i]); + } + break; + } + case IDP_GROUP: + for (IDProperty *loop = prop->data.group.first; loop; loop = loop->next) { + expand_idprops(fd, mainvar, loop); + } + break; + } +} + static void expand_fmodifiers(FileData *fd, Main *mainvar, ListBase *list) { FModifier *fcm; @@ -9090,6 +9151,7 @@ static void expand_key(FileData *fd, Main *mainvar, Key *key) static void expand_nodetree(FileData *fd, Main *mainvar, bNodeTree *ntree) { bNode *node; + bNodeSocket *sock; if (ntree->adt) expand_animdata(fd, mainvar, ntree->adt); @@ -9098,10 +9160,22 @@ static void expand_nodetree(FileData *fd, Main *mainvar, bNodeTree *ntree) expand_doit(fd, mainvar, ntree->gpd); for (node = ntree->nodes.first; node; node = node->next) { - if (node->id && node->type != CMP_NODE_R_LAYERS) + if (node->id && node->type != CMP_NODE_R_LAYERS) { expand_doit(fd, mainvar, node->id); + } + + expand_idprops(fd, mainvar, node->prop); + + for (sock = node->inputs.first; sock; sock = sock->next) + expand_doit(fd, mainvar, sock->prop); + for (sock = node->outputs.first; sock; sock = sock->next) + expand_doit(fd, mainvar, sock->prop); } + for (sock = ntree->inputs.first; sock; sock = sock->next) + expand_doit(fd, mainvar, sock->prop); + for (sock = ntree->outputs.first; sock; sock = sock->next) + expand_doit(fd, mainvar, sock->prop); } static void expand_texture(FileData *fd, Main *mainvar, Tex *tex) @@ -9319,17 +9393,6 @@ static void expand_constraints(FileData *fd, Main *mainvar, ListBase *lb) } } -#if 0 /* Disabled as it doesn't actually do anything except recurse... */ -static void expand_bones(FileData *fd, Main *mainvar, Bone *bone) -{ - Bone *curBone; - - for (curBone = bone->childbase.first; curBone; curBone=curBone->next) { - expand_bones(fd, mainvar, curBone); - } -} -#endif - static void expand_pose(FileData *fd, Main *mainvar, bPose *pose) { bPoseChannel *chan; @@ -9339,24 +9402,28 @@ static void expand_pose(FileData *fd, Main *mainvar, bPose *pose) for (chan = pose->chanbase.first; chan; chan = chan->next) { expand_constraints(fd, mainvar, &chan->constraints); + expand_idprops(fd, mainvar, chan->prop); expand_doit(fd, mainvar, chan->custom); } } +static void expand_bones(FileData *fd, Main *mainvar, Bone *bone) +{ + expand_idprops(fd, mainvar, bone->prop); + + for (Bone *curBone = bone->childbase.first; curBone; curBone = curBone->next) { + expand_bones(fd, mainvar, curBone); + } +} + static void expand_armature(FileData *fd, Main *mainvar, bArmature *arm) -{ +{ if (arm->adt) expand_animdata(fd, mainvar, arm->adt); - -#if 0 /* Disabled as this currently only recurses down the chain doing nothing */ - { - Bone *curBone; - - for (curBone = arm->bonebase.first; curBone; curBone=curBone->next) { - expand_bones(fd, mainvar, curBone); - } + + for (Bone *curBone = arm->bonebase.first; curBone; curBone = curBone->next) { + expand_bones(fd, mainvar, curBone); } -#endif } static void expand_object_expandModifiers( @@ -9584,6 +9651,8 @@ static void expand_scene(FileData *fd, Main *mainvar, Scene *sce) SEQ_BEGIN (sce->ed, seq) { + expand_idprops(fd, mainvar, seq->prop); + if (seq->scene) expand_doit(fd, mainvar, seq->scene); if (seq->scene_camera) expand_doit(fd, mainvar, seq->scene_camera); if (seq->clip) expand_doit(fd, mainvar, seq->clip); @@ -9741,6 +9810,8 @@ void BLO_expand_main(void *fdhandle, Main *mainvar) id = lbarray[a]->first; while (id) { if (id->tag & LIB_TAG_NEED_EXPAND) { + expand_idprops(fd, mainvar, id->properties); + switch (GS(id->name)) { case ID_OB: expand_object(fd, mainvar, (Object *)id); diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index b5fbe4ba586..861e249b0ee 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -72,6 +72,7 @@ #include "BKE_global.h" #include "BKE_group.h" #include "BKE_fcurve.h" +#include "BKE_idprop.h" #include "BKE_lamp.h" #include "BKE_lattice.h" #include "BKE_library.h" @@ -2101,6 +2102,34 @@ void ED_object_single_users(Main *bmain, Scene *scene, const bool full, const bo single_tex_users_expand(bmain); } + /* Relink datablock pointer properties */ + { + IDP_RelinkProperty(scene->id.properties); + + for (Base *base = scene->base.first; base; base = base->next) { + Object *ob = base->object; + if (!ID_IS_LINKED_DATABLOCK(ob)) { + IDP_RelinkProperty(ob->id.properties); + } + } + + if (scene->nodetree) { + IDP_RelinkProperty(scene->nodetree->id.properties); + for (bNode *node = scene->nodetree->nodes.first; node; node = node->next) { + IDP_RelinkProperty(node->prop); + } + } + + if (scene->gpd) { + IDP_RelinkProperty(scene->gpd->id.properties); + } + + IDP_RelinkProperty(scene->world->id.properties); + + if (scene->clip) { + IDP_RelinkProperty(scene->clip->id.properties); + } + } BKE_main_id_clear_newpoins(bmain); DAG_relations_tag_update(bmain); } diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index 2c6f3d2fc66..da0f505c4f3 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -82,8 +82,6 @@ enum { IDP_FLOAT = 2, IDP_ARRAY = 5, IDP_GROUP = 6, - /* the ID link property type hasn't been implemented yet, this will require - * some cleanup of blenkernel, most likely. */ IDP_ID = 7, IDP_DOUBLE = 8, IDP_IDPARRAY = 9, diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h index f9aaec69ce7..a1af3f98274 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -767,6 +767,8 @@ void RNA_struct_blender_type_set(StructRNA *srna, void *blender_type); struct IDProperty *RNA_struct_idprops(PointerRNA *ptr, bool create); bool RNA_struct_idprops_check(StructRNA *srna); bool RNA_struct_idprops_register_check(const StructRNA *type); +bool RNA_struct_idprops_datablock_allowed(const StructRNA *type); +bool RNA_struct_idprops_contains_datablock(const StructRNA *type); bool RNA_struct_idprops_unset(PointerRNA *ptr, const char *identifier); PropertyRNA *RNA_struct_find_property(PointerRNA *ptr, const char *identifier); diff --git a/source/blender/makesrna/RNA_define.h b/source/blender/makesrna/RNA_define.h index 2a680b6eaeb..6e62313b00a 100644 --- a/source/blender/makesrna/RNA_define.h +++ b/source/blender/makesrna/RNA_define.h @@ -167,6 +167,7 @@ void RNA_def_property_editable_func(PropertyRNA *prop, const char *editable); void RNA_def_property_editable_array_func(PropertyRNA *prop, const char *editable); void RNA_def_property_update_runtime(PropertyRNA *prop, const void *func); +void RNA_def_property_poll_runtime(PropertyRNA *prop, const void *func); void RNA_def_property_dynamic_array_funcs(PropertyRNA *prop, const char *getlength); void RNA_def_property_boolean_funcs(PropertyRNA *prop, const char *get, const char *set); diff --git a/source/blender/makesrna/RNA_types.h b/source/blender/makesrna/RNA_types.h index dee8df7d933..cd04f9e8a6d 100644 --- a/source/blender/makesrna/RNA_types.h +++ b/source/blender/makesrna/RNA_types.h @@ -434,6 +434,8 @@ typedef enum StructFlag { STRUCT_GENERATED = (1 << 4), STRUCT_FREE_POINTERS = (1 << 5), STRUCT_NO_IDPROPERTIES = (1 << 6), /* Menus and Panels don't need properties */ + STRUCT_NO_DATABLOCK_IDPROPERTIES = (1 << 7), /* e.g. for Operator */ + STRUCT_CONTAINS_DATABLOCK_IDPROPERTIES = (1 << 8), /* for PropertyGroup which contains pointers to datablocks */ } StructFlag; typedef int (*StructValidateFunc)(struct PointerRNA *ptr, void *data, int *have_function); diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c index 671902c5cc7..6ed2c55138c 100644 --- a/source/blender/makesrna/intern/rna_ID.c +++ b/source/blender/makesrna/intern/rna_ID.c @@ -802,7 +802,11 @@ static void rna_def_ID_properties(BlenderRNA *brna) RNA_def_struct_name_property(srna, prop); #endif - /* IDP_ID -- not implemented yet in id properties */ + /* IDP_ID */ + prop = RNA_def_property(srna, "id", PROP_POINTER, PROP_NONE); + RNA_def_property_flag(prop, PROP_EXPORT | PROP_IDPROPERTY | PROP_NEVER_UNLINK); + RNA_def_property_struct_type(prop, "ID"); + /* ID property groups > level 0, since level 0 group is merged * with native RNA properties. the builtin_properties will take diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index 7266cfb12d6..c3d2d92fc5e 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -50,6 +50,7 @@ #include "BKE_idcode.h" #include "BKE_idprop.h" #include "BKE_fcurve.h" +#include "BKE_library.h" #include "BKE_main.h" #include "BKE_report.h" @@ -380,6 +381,7 @@ static bool rna_idproperty_verify_valid(PointerRNA *ptr, PropertyRNA *prop, IDPr return false; break; case IDP_GROUP: + case IDP_ID: if (prop->type != PROP_POINTER) return false; break; @@ -395,7 +397,8 @@ static PropertyRNA *typemap[IDP_NUMTYPES] = { (PropertyRNA *)&rna_PropertyGroupItem_int, (PropertyRNA *)&rna_PropertyGroupItem_float, NULL, NULL, NULL, - (PropertyRNA *)&rna_PropertyGroupItem_group, NULL, + (PropertyRNA *)&rna_PropertyGroupItem_group, + (PropertyRNA *)&rna_PropertyGroupItem_id, (PropertyRNA *)&rna_PropertyGroupItem_double, (PropertyRNA *)&rna_PropertyGroupItem_idp_array }; @@ -587,6 +590,21 @@ bool RNA_struct_idprops_register_check(const StructRNA *type) return (type->flag & STRUCT_NO_IDPROPERTIES) == 0; } +bool RNA_struct_idprops_datablock_allowed(const StructRNA *type) +{ + return (type->flag & (STRUCT_NO_DATABLOCK_IDPROPERTIES | STRUCT_NO_IDPROPERTIES)) == 0; +} + +/** + * Whether given type implies datablock usage by IDProperties. + * This is used to prevent classes allowed to have IDProperties, but not datablock ones, to indirectly use some + * (e.g. by assigning an IDP_GROUP containing some IDP_ID pointers...). + */ +bool RNA_struct_idprops_contains_datablock(const StructRNA *type) +{ + return (type->flag & (STRUCT_CONTAINS_DATABLOCK_IDPROPERTIES | STRUCT_ID)) != 0; +} + /* remove an id-property */ bool RNA_struct_idprops_unset(PointerRNA *ptr, const char *identifier) { @@ -628,8 +646,11 @@ PropertyRNA *RNA_struct_find_property(PointerRNA *ptr, const char *identifier) /* id prop lookup, not so common */ PropertyRNA *r_prop = NULL; PointerRNA r_ptr; /* only support single level props */ - if (RNA_path_resolve(ptr, identifier, &r_ptr, &r_prop) && (r_ptr.type == ptr->type) && (r_ptr.data == ptr->data)) + if (RNA_path_resolve_property(ptr, identifier, &r_ptr, &r_prop) && + (r_ptr.type == ptr->type) && (r_ptr.data == ptr->data)) + { return r_prop; + } } else { /* most common case */ @@ -1201,13 +1222,20 @@ int RNA_property_pointer_poll(PointerRNA *ptr, PropertyRNA *prop, PointerRNA *va if (prop->type == PROP_POINTER) { PointerPropertyRNA *pprop = (PointerPropertyRNA *)prop; - if (pprop->poll) - return pprop->poll(ptr, *value); + + if (pprop->poll) { + if (rna_idproperty_check(&prop, ptr)) { + return ((PropPointerPollFuncPy) pprop->poll)(ptr, *value, prop); + } + else { + return pprop->poll(ptr, *value); + } + } return 1; } - printf("%s %s: is not a pointer property.\n", __func__, prop->identifier); + printf("%s: %s is not a pointer property.\n", __func__, prop->identifier); return 0; } @@ -2967,6 +2995,10 @@ PointerRNA RNA_property_pointer_get(PointerRNA *ptr, PropertyRNA *prop) if ((idprop = rna_idproperty_check(&prop, ptr))) { pprop = (PointerPropertyRNA *)prop; + if (RNA_struct_is_ID(pprop->type)) { + return rna_pointer_inherit_refine(ptr, pprop->type, IDP_Id(idprop)); + } + /* for groups, data is idprop itself */ if (pprop->typef) return rna_pointer_inherit_refine(ptr, pprop->typef(ptr), idprop); @@ -2989,22 +3021,32 @@ PointerRNA RNA_property_pointer_get(PointerRNA *ptr, PropertyRNA *prop) void RNA_property_pointer_set(PointerRNA *ptr, PropertyRNA *prop, PointerRNA ptr_value) { - /*IDProperty *idprop;*/ - + PointerPropertyRNA *pprop = (PointerPropertyRNA *)prop; BLI_assert(RNA_property_type(prop) == PROP_POINTER); - if ((/*idprop = */ rna_idproperty_check(&prop, ptr))) { - /* not supported */ - /* rna_idproperty_touch(idprop); */ + /* Check types */ + if (ptr_value.type != NULL && !RNA_struct_is_a(ptr_value.type, pprop->type)) { + printf("%s: expected %s type, not %s.\n", __func__, pprop->type->identifier, ptr_value.type->identifier); + return; } - else { - PointerPropertyRNA *pprop = (PointerPropertyRNA *)prop; - if (pprop->set && - !((prop->flag & PROP_NEVER_NULL) && ptr_value.data == NULL) && - !((prop->flag & PROP_ID_SELF_CHECK) && ptr->id.data == ptr_value.id.data)) - { - pprop->set(ptr, ptr_value); + /* RNA */ + if (pprop->set && + !((prop->flag & PROP_NEVER_NULL) && ptr_value.data == NULL) && + !((prop->flag & PROP_ID_SELF_CHECK) && ptr->id.data == ptr_value.id.data)) + { + pprop->set(ptr, ptr_value); + } + /* IDProperty */ + else if (prop->flag & PROP_EDITABLE) { + IDPropertyTemplate val = {0}; + IDProperty *group; + + val.id = ptr_value.data; + + group = RNA_struct_idprops(ptr, true); + if (group) { + IDP_ReplaceInGroup(group, IDP_New(IDP_ID, &val, prop->identifier)); } } } diff --git a/source/blender/makesrna/intern/rna_define.c b/source/blender/makesrna/intern/rna_define.c index 1d232d2df39..49983b0bc2b 100644 --- a/source/blender/makesrna/intern/rna_define.c +++ b/source/blender/makesrna/intern/rna_define.c @@ -2168,6 +2168,16 @@ void RNA_def_property_update_runtime(PropertyRNA *prop, const void *func) prop->update = (void *)func; } +void RNA_def_property_poll_runtime(PropertyRNA *prop, const void *func) +{ + if (prop->type == PROP_POINTER) { + ((PointerPropertyRNA *)prop)->poll = func; + } + else { + fprintf(stderr, "%s: %s is not a Pointer Property.\n", __func__, prop->identifier); + } +} + void RNA_def_property_dynamic_array_funcs(PropertyRNA *prop, const char *getlength) { if (!DefRNA.preprocess) { @@ -2982,6 +2992,9 @@ PropertyRNA *RNA_def_pointer_runtime(StructOrFunctionRNA *cont_, const char *ide prop = RNA_def_property(cont, identifier, PROP_POINTER, PROP_NONE); RNA_def_property_struct_runtime(prop, type); + if ((type->flag & STRUCT_ID) != 0) { + prop->flag |= PROP_EDITABLE; + } RNA_def_property_ui_text(prop, ui_name, ui_description); return prop; diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h index 76455adbc78..dfd5af788f6 100644 --- a/source/blender/makesrna/intern/rna_internal.h +++ b/source/blender/makesrna/intern/rna_internal.h @@ -344,6 +344,7 @@ extern IntPropertyRNA rna_PropertyGroupItem_int_array; extern FloatPropertyRNA rna_PropertyGroupItem_float; extern FloatPropertyRNA rna_PropertyGroupItem_float_array; extern PointerPropertyRNA rna_PropertyGroupItem_group; +extern PointerPropertyRNA rna_PropertyGroupItem_id; extern CollectionPropertyRNA rna_PropertyGroupItem_collection; extern CollectionPropertyRNA rna_PropertyGroupItem_idp_array; extern FloatPropertyRNA rna_PropertyGroupItem_double; diff --git a/source/blender/makesrna/intern/rna_internal_types.h b/source/blender/makesrna/intern/rna_internal_types.h index fce81e6967e..df591659fdb 100644 --- a/source/blender/makesrna/intern/rna_internal_types.h +++ b/source/blender/makesrna/intern/rna_internal_types.h @@ -94,6 +94,7 @@ typedef PointerRNA (*PropPointerGetFunc)(struct PointerRNA *ptr); typedef StructRNA *(*PropPointerTypeFunc)(struct PointerRNA *ptr); typedef void (*PropPointerSetFunc)(struct PointerRNA *ptr, const PointerRNA value); typedef int (*PropPointerPollFunc)(struct PointerRNA *ptr, const PointerRNA value); +typedef int (*PropPointerPollFuncPy)(struct PointerRNA *ptr, const PointerRNA value, const PropertyRNA *prop); typedef void (*PropCollectionBeginFunc)(struct CollectionPropertyIterator *iter, struct PointerRNA *ptr); typedef void (*PropCollectionNextFunc)(struct CollectionPropertyIterator *iter); typedef void (*PropCollectionEndFunc)(struct CollectionPropertyIterator *iter); diff --git a/source/blender/makesrna/intern/rna_ui.c b/source/blender/makesrna/intern/rna_ui.c index 7a3c862f04c..54b82fc89d6 100644 --- a/source/blender/makesrna/intern/rna_ui.c +++ b/source/blender/makesrna/intern/rna_ui.c @@ -1039,6 +1039,7 @@ static void rna_def_uilist(BlenderRNA *brna) RNA_def_struct_refine_func(srna, "rna_UIList_refine"); RNA_def_struct_register_funcs(srna, "rna_UIList_register", "rna_UIList_unregister", NULL); RNA_def_struct_idprops_func(srna, "rna_UIList_idprops"); + RNA_def_struct_flag(srna, STRUCT_NO_DATABLOCK_IDPROPERTIES); /* Registration */ prop = RNA_def_property(srna, "bl_idname", PROP_STRING, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index 4d70b87843a..d1e89ea18d0 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -3156,6 +3156,7 @@ static void rna_def_userdef_addon_pref(BlenderRNA *brna) RNA_def_struct_refine_func(srna, "rna_AddonPref_refine"); RNA_def_struct_register_funcs(srna, "rna_AddonPref_register", "rna_AddonPref_unregister", NULL); RNA_def_struct_idprops_func(srna, "rna_AddonPref_idprops"); + RNA_def_struct_flag(srna, STRUCT_NO_DATABLOCK_IDPROPERTIES); /* Mandatory! */ /* registration */ RNA_define_verify_sdna(0); diff --git a/source/blender/makesrna/intern/rna_wm.c b/source/blender/makesrna/intern/rna_wm.c index 35c9c9bcc89..b458b2e69d5 100644 --- a/source/blender/makesrna/intern/rna_wm.c +++ b/source/blender/makesrna/intern/rna_wm.c @@ -1556,6 +1556,7 @@ static void rna_def_operator(BlenderRNA *brna) RNA_def_struct_ui_text(srna, "Operator Properties", "Input properties of an Operator"); RNA_def_struct_refine_func(srna, "rna_OperatorProperties_refine"); RNA_def_struct_idprops_func(srna, "rna_OperatorProperties_idprops"); + RNA_def_struct_flag(srna, STRUCT_NO_DATABLOCK_IDPROPERTIES); } static void rna_def_macro_operator(BlenderRNA *brna) diff --git a/source/blender/python/generic/idprop_py_api.c b/source/blender/python/generic/idprop_py_api.c index 0a9931f2683..5d6a7c578a2 100644 --- a/source/blender/python/generic/idprop_py_api.c +++ b/source/blender/python/generic/idprop_py_api.c @@ -43,6 +43,9 @@ #include "python_utildefines.h" +extern bool pyrna_id_FromPyObject(PyObject *obj, ID **id); +extern PyObject *pyrna_id_CreatePyObject(ID *id); +extern bool pyrna_id_CheckPyObject(PyObject *obj); /*********************** ID Property Main Wrapper Stuff ***************/ @@ -88,6 +91,11 @@ static PyObject *idprop_py_from_idp_group(ID *id, IDProperty *prop, IDProperty * return (PyObject *)group; } +static PyObject *idprop_py_from_idp_id(IDProperty *prop) +{ + return pyrna_id_CreatePyObject(prop->data.pointer); +} + static PyObject *idprop_py_from_idp_array(ID *id, IDProperty *prop) { BPy_IDProperty *array = PyObject_New(BPy_IDProperty, &BPy_IDArray_Type); @@ -148,6 +156,7 @@ PyObject *BPy_IDGroup_WrapData(ID *id, IDProperty *prop, IDProperty *parent) case IDP_GROUP: return idprop_py_from_idp_group(id, prop, parent); case IDP_ARRAY: return idprop_py_from_idp_array(id, prop); case IDP_IDPARRAY: return idprop_py_from_idp_idparray(id, prop); /* this could be better a internal type */ + case IDP_ID: return idprop_py_from_idp_id(prop); default: Py_RETURN_NONE; } } @@ -586,8 +595,15 @@ static IDProperty *idp_from_PyMapping(const char *name, PyObject *ob) return prop; } +static IDProperty *idp_from_DatablockPointer(const char *name, PyObject *ob, IDPropertyTemplate *val) +{ + pyrna_id_FromPyObject(ob, &val->id); + return IDP_New(IDP_ID, val, name); +} + static IDProperty *idp_from_PyObject(PyObject *name_obj, PyObject *ob) { + IDPropertyTemplate val = {0}; const char *name = idp_try_read_name(name_obj); if (name == NULL) { return NULL; @@ -608,6 +624,9 @@ static IDProperty *idp_from_PyObject(PyObject *name_obj, PyObject *ob) else if (PySequence_Check(ob)) { return idp_from_PySequence(name, ob); } + else if (ob == Py_None || pyrna_id_CheckPyObject(ob)) { + return idp_from_DatablockPointer(name, ob, &val); + } else if (PyMapping_Check(ob)) { return idp_from_PyMapping(name, ob); } @@ -732,6 +751,8 @@ static PyObject *BPy_IDGroup_MapDataToPy(IDProperty *prop) return idprop_py_from_idp_float(prop); case IDP_DOUBLE: return idprop_py_from_idp_double(prop); + case IDP_ID: + return idprop_py_from_idp_id(prop); case IDP_ARRAY: { PyObject *seq = PyList_New(prop->len); diff --git a/source/blender/python/intern/bpy_props.c b/source/blender/python/intern/bpy_props.c index f7348ac2250..2656e612b18 100644 --- a/source/blender/python/intern/bpy_props.c +++ b/source/blender/python/intern/bpy_props.c @@ -50,11 +50,13 @@ #include "../generic/py_capi_utils.h" /* initial definition of callback slots we'll probably have more than 1 */ -#define BPY_DATA_CB_SLOT_SIZE 3 - -#define BPY_DATA_CB_SLOT_UPDATE 0 -#define BPY_DATA_CB_SLOT_GET 1 -#define BPY_DATA_CB_SLOT_SET 2 +enum { + BPY_DATA_CB_SLOT_UPDATE = 0, + BPY_DATA_CB_SLOT_GET = 1, + BPY_DATA_CB_SLOT_SET = 2, + BPY_DATA_CB_SLOT_POLL = 3, + BPY_DATA_CB_SLOT_SIZE = 4, +}; extern BPy_StructRNA *bpy_context_module; @@ -71,6 +73,9 @@ static EnumPropertyItem property_flag_items[] = { " :arg options: Enumerator in ['HIDDEN', 'SKIP_SAVE', 'ANIMATABLE', 'LIBRARY_EDITABLE', 'PROPORTIONAL'," \ "'TEXTEDIT_UPDATE'].\n" \ " :type options: set\n" \ +" :arg poll: function to be called to determine whether an item is valid for this property.\n" \ +" The function must take 2 values (self,object) and return Bool.\n" \ +" :type poll: function\n" \ static EnumPropertyItem property_flag_enum_items[] = { {PROP_HIDDEN, "HIDDEN", 0, "Hidden", ""}, @@ -389,6 +394,51 @@ static void bpy_prop_boolean_set_cb(struct PointerRNA *ptr, struct PropertyRNA * } } +static int bpy_prop_poll_cb(struct PointerRNA *self, PointerRNA candidate, struct PropertyRNA *prop) +{ + PyObject *py_self; + PyObject *py_candidate; + PyObject *py_func; + PyObject **py_data = RNA_property_py_data_get(prop); + PyObject *args; + PyObject *ret; + bool result; + const int is_write_ok = pyrna_write_check(); + PyGILState_STATE gilstate = PyGILState_Ensure(); + + BLI_assert(self != NULL); + + py_self = pyrna_struct_as_instance(self); + py_candidate = pyrna_struct_as_instance(&candidate); + py_func = py_data[BPY_DATA_CB_SLOT_POLL]; + + if (!is_write_ok) + pyrna_write_set(true); + + args = PyTuple_New(2); + PyTuple_SET_ITEM(args, 0, py_self); + PyTuple_SET_ITEM(args, 1, py_candidate); + + ret = PyObject_CallObject(py_func, args); + + Py_DECREF(args); + + if (ret == NULL) { + printf_func_error(py_func); + result = false; + } + else { + result = PyObject_IsTrue(ret); + Py_DECREF(ret); + } + + PyGILState_Release(gilstate); + if (!is_write_ok) + pyrna_write_set(false); + + return result; +} + static void bpy_prop_boolean_array_get_cb(struct PointerRNA *ptr, struct PropertyRNA *prop, int *values) { PyObject **py_data = RNA_property_py_data_get(prop); @@ -1598,6 +1648,16 @@ static void bpy_prop_callback_assign_update(struct PropertyRNA *prop, PyObject * } } +static void bpy_prop_callback_assign_pointer(struct PropertyRNA *prop, PyObject *poll_cb) +{ + if (poll_cb && poll_cb != Py_None) { + PyObject **py_data = bpy_prop_py_data_get(prop); + + RNA_def_property_poll_runtime(prop, (void *) bpy_prop_poll_cb); + py_data[BPY_DATA_CB_SLOT_POLL] = poll_cb; + } +} + static void bpy_prop_callback_assign_boolean(struct PropertyRNA *prop, PyObject *get_cb, PyObject *set_cb) { BooleanPropertyGetFunc rna_get_cb = NULL; @@ -1904,7 +1964,7 @@ static void bpy_prop_callback_assign_enum(struct PropertyRNA *prop, PyObject *ge " :type set: function\n" \ #define BPY_PROPDEF_TYPE_DOC \ -" :arg type: A subclass of :class:`bpy.types.PropertyGroup`.\n" \ +" :arg type: A subclass of :class:`bpy.types.PropertyGroup` or :class:`bpy.types.ID`.\n" \ " :type type: class\n" \ #if 0 @@ -2772,7 +2832,7 @@ static PyObject *BPy_EnumProperty(PyObject *self, PyObject *args, PyObject *kw) Py_RETURN_NONE; } -static StructRNA *pointer_type_from_py(PyObject *value, const char *error_prefix) +StructRNA *pointer_type_from_py(PyObject *value, const char *error_prefix) { StructRNA *srna; @@ -2782,25 +2842,18 @@ static StructRNA *pointer_type_from_py(PyObject *value, const char *error_prefix PyObject *msg = PyC_ExceptionBuffer(); const char *msg_char = _PyUnicode_AsString(msg); PyErr_Format(PyExc_TypeError, - "%.200s expected an RNA type derived from PropertyGroup, failed with: %s", + "%.200s expected an RNA type, failed with: %s", error_prefix, msg_char); Py_DECREF(msg); } else { PyErr_Format(PyExc_TypeError, - "%.200s expected an RNA type derived from PropertyGroup, failed with type '%s'", + "%.200s expected an RNA type, failed with type '%s'", error_prefix, Py_TYPE(value)->tp_name); } return NULL; } - if (!RNA_struct_is_a(srna, &RNA_PropertyGroup)) { - PyErr_Format(PyExc_TypeError, - "%.200s expected an RNA type derived from PropertyGroup", - error_prefix); - return NULL; - } - return srna; } @@ -2809,7 +2862,8 @@ PyDoc_STRVAR(BPy_PointerProperty_doc, "name=\"\", " "description=\"\", " "options={'ANIMATABLE'}, " - "update=None)\n" + "update=None,\n" + "poll=None)\n" "\n" " Returns a new pointer property definition.\n" "\n" @@ -2819,14 +2873,14 @@ BPY_PROPDEF_DESC_DOC BPY_PROPDEF_OPTIONS_DOC BPY_PROPDEF_UPDATE_DOC ); -static PyObject *BPy_PointerProperty(PyObject *self, PyObject *args, PyObject *kw) +PyObject *BPy_PointerProperty(PyObject *self, PyObject *args, PyObject *kw) { StructRNA *srna; BPY_PROPDEF_HEAD(PointerProperty); if (srna) { - static const char *kwlist[] = {"attr", "type", "name", "description", "options", "update", NULL}; + static const char *kwlist[] = {"attr", "type", "name", "description", "options", "poll", "update", NULL}; const char *id = NULL, *name = NULL, *description = ""; int id_len; PropertyRNA *prop; @@ -2834,33 +2888,47 @@ static PyObject *BPy_PointerProperty(PyObject *self, PyObject *args, PyObject *k PyObject *type = Py_None; PyObject *pyopts = NULL; int opts = 0; - PyObject *update_cb = NULL; + PyObject *update_cb = NULL, *poll_cb = NULL; if (!PyArg_ParseTupleAndKeywords(args, kw, - "s#O|ssO!O:PointerProperty", + "s#O|ssO!OOO:PointerProperty", (char **)kwlist, &id, &id_len, &type, &name, &description, &PySet_Type, &pyopts, - &update_cb)) + &poll_cb, &update_cb)) { return NULL; } BPY_PROPDEF_CHECK(PointerProperty, property_flag_items); - ptype = pointer_type_from_py(type, "PointerProperty(...):"); + ptype = pointer_type_from_py(type, "PointerProperty(...)"); if (!ptype) return NULL; - + if (!RNA_struct_is_a(ptype, &RNA_PropertyGroup) && !RNA_struct_is_ID(ptype)) { + PyErr_Format(PyExc_TypeError, + "PointerProperty(...) expected an RNA type derived from %.200s or %.200s", + RNA_struct_ui_name(&RNA_ID), RNA_struct_ui_name(&RNA_PropertyGroup)); + return NULL; + } if (bpy_prop_callback_check(update_cb, "update", 2) == -1) { return NULL; } - + if (bpy_prop_callback_check(poll_cb, "poll", 2) == -1) { + return NULL; + } prop = RNA_def_pointer_runtime(srna, id, ptype, name ? name : id, description); if (pyopts) { bpy_prop_assign_flag(prop, opts); } + + if (RNA_struct_idprops_contains_datablock(ptype)) { + if (RNA_struct_is_a(srna, &RNA_PropertyGroup)) { + RNA_def_struct_flag(srna, STRUCT_CONTAINS_DATABLOCK_IDPROPERTIES); + } + } bpy_prop_callback_assign_update(prop, update_cb); + bpy_prop_callback_assign_pointer(prop, poll_cb); RNA_def_property_duplicate_pointers(srna, prop); } Py_RETURN_NONE; @@ -2879,7 +2947,7 @@ BPY_PROPDEF_NAME_DOC BPY_PROPDEF_DESC_DOC BPY_PROPDEF_OPTIONS_DOC ); -static PyObject *BPy_CollectionProperty(PyObject *self, PyObject *args, PyObject *kw) +PyObject *BPy_CollectionProperty(PyObject *self, PyObject *args, PyObject *kw) { StructRNA *srna; @@ -2910,10 +2978,23 @@ static PyObject *BPy_CollectionProperty(PyObject *self, PyObject *args, PyObject if (!ptype) return NULL; + if (!RNA_struct_is_a(ptype, &RNA_PropertyGroup)) { + PyErr_Format(PyExc_TypeError, + "CollectionProperty(...) expected an RNA type derived from %.200s", + RNA_struct_ui_name(&RNA_ID), RNA_struct_ui_name(&RNA_PropertyGroup)); + return NULL; + } + prop = RNA_def_collection_runtime(srna, id, ptype, name ? name : id, description); if (pyopts) { bpy_prop_assign_flag(prop, opts); } + + if (RNA_struct_idprops_contains_datablock(ptype)) { + if (RNA_struct_is_a(srna, &RNA_PropertyGroup)) { + RNA_def_struct_flag(srna, STRUCT_CONTAINS_DATABLOCK_IDPROPERTIES); + } + } RNA_def_property_duplicate_pointers(srna, prop); } Py_RETURN_NONE; diff --git a/source/blender/python/intern/bpy_props.h b/source/blender/python/intern/bpy_props.h index c9934ca0cf3..614c1b4b708 100644 --- a/source/blender/python/intern/bpy_props.h +++ b/source/blender/python/intern/bpy_props.h @@ -30,6 +30,10 @@ PyObject *BPY_rna_props(void); +PyObject *BPy_PointerProperty(PyObject *self, PyObject *args, PyObject *kw); +PyObject *BPy_CollectionProperty(PyObject *self, PyObject *args, PyObject *kw); +StructRNA *pointer_type_from_py(PyObject *value, const char *error_prefix); + #define PYRNA_STACK_ARRAY 32 #endif diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index 2fd46ab94f0..00627030db3 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -1934,16 +1934,10 @@ static int pyrna_py_to_prop( } else { /* data == NULL, assign to RNA */ - if (value == Py_None) { - PointerRNA valueptr = {{NULL}}; - RNA_property_pointer_set(ptr, prop, valueptr); - } - else if (RNA_struct_is_a(param->ptr.type, ptr_type)) { - RNA_property_pointer_set(ptr, prop, param->ptr); - } - else { + if (value == Py_None || RNA_struct_is_a(param->ptr.type, ptr_type)) + RNA_property_pointer_set(ptr, prop, value == Py_None ? PointerRNA_NULL : param->ptr); + else raise_error = true; - } } if (raise_error) { @@ -3277,6 +3271,16 @@ static int pyrna_struct_ass_subscript(BPy_StructRNA *self, PyObject *key, PyObje return -1; } + BPy_StructRNA *val = (BPy_StructRNA *)value; + if (val && self->ptr.type && val->ptr.type) { + if (!RNA_struct_idprops_datablock_allowed(self->ptr.type) && + RNA_struct_idprops_contains_datablock(val->ptr.type)) + { + PyErr_SetString(PyExc_TypeError, "bpy_struct[key] = val: datablock id properties not supported for this type"); + return -1; + } + } + return BPy_Wrap_SetMapItem(group, key, value); } @@ -6745,7 +6749,7 @@ PyObject *pyrna_id_CreatePyObject(ID *id) bool pyrna_id_FromPyObject(PyObject *obj, ID **id) { - if (BPy_StructRNA_Check(obj) && (RNA_struct_is_ID(((BPy_StructRNA *)obj)->ptr.type))) { + if (pyrna_id_CheckPyObject(obj)) { *id = ((BPy_StructRNA *)obj)->ptr.id.data; return true; } @@ -6755,6 +6759,11 @@ bool pyrna_id_FromPyObject(PyObject *obj, ID **id) } } +bool pyrna_id_CheckPyObject(PyObject *obj) +{ + return BPy_StructRNA_Check(obj) && (RNA_struct_is_ID(((BPy_StructRNA *) obj)->ptr.type)); +} + void BPY_rna_init(void) { #ifdef USE_MATHUTILS /* register mathutils callbacks, ok to run more than once. */ @@ -7089,6 +7098,21 @@ static int deferred_register_prop(StructRNA *srna, PyObject *key, PyObject *item args_fake = PyTuple_New(1); PyTuple_SET_ITEM(args_fake, 0, py_srna_cobject); + PyObject *type = PyDict_GetItemString(py_kw, "type"); + StructRNA *type_srna = srna_from_self(type, ""); + if (type_srna) { + if (!RNA_struct_idprops_datablock_allowed(srna) && + (*(PyCFunctionWithKeywords)PyCFunction_GET_FUNCTION(py_func) == BPy_PointerProperty || + *(PyCFunctionWithKeywords)PyCFunction_GET_FUNCTION(py_func) == BPy_CollectionProperty) && + RNA_struct_idprops_contains_datablock(type_srna)) + { + PyErr_Format(PyExc_ValueError, + "bpy_struct \"%.200s\" doesn't support datablock properties \n", + RNA_struct_identifier(srna)); + return -1; + } + } + py_ret = PyObject_Call(py_func, args_fake, py_kw); if (py_ret) { diff --git a/source/blender/python/intern/bpy_rna.h b/source/blender/python/intern/bpy_rna.h index e38d4f095d6..605f79b1ad8 100644 --- a/source/blender/python/intern/bpy_rna.h +++ b/source/blender/python/intern/bpy_rna.h @@ -179,6 +179,7 @@ PyObject *pyrna_prop_CreatePyObject(PointerRNA *ptr, PropertyRNA *prop); /* extern'd by other modules which don't deal closely with RNA */ PyObject *pyrna_id_CreatePyObject(struct ID *id); bool pyrna_id_FromPyObject(PyObject *obj, struct ID **id); +bool pyrna_id_CheckPyObject(PyObject *obj); /* operators also need this to set args */ int pyrna_pydict_to_props(PointerRNA *ptr, PyObject *kw, const bool all_args, const char *error_prefix); diff --git a/source/blender/windowmanager/intern/wm.c b/source/blender/windowmanager/intern/wm.c index 4351cd22b18..d0522fdd7d4 100644 --- a/source/blender/windowmanager/intern/wm.c +++ b/source/blender/windowmanager/intern/wm.c @@ -489,7 +489,7 @@ void wm_close_and_free_all(bContext *C, ListBase *wmlist) while ((wm = wmlist->first)) { wm_close_and_free(C, wm); BLI_remlink(wmlist, wm); - BKE_libblock_free_data(bmain, &wm->id); + BKE_libblock_free_data(bmain, &wm->id, true); MEM_freeN(wm); } } -- cgit v1.2.3