Datablock ID Properties
authorAlexander Romanov <a.romanov@blend4web.com>
Thu, 13 Apr 2017 09:30:03 +0000 (12:30 +0300)
committerAlexander Romanov <a.romanov@blend4web.com>
Thu, 13 Apr 2017 09:33:05 +0000 (12:33 +0300)
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

29 files changed:
source/blender/blenkernel/BKE_idprop.h
source/blender/blenkernel/BKE_library.h
source/blender/blenkernel/BKE_library_query.h
source/blender/blenkernel/intern/idprop.c
source/blender/blenkernel/intern/library_query.c
source/blender/blenkernel/intern/library_remap.c
source/blender/blenkernel/intern/node.c
source/blender/blenloader/intern/readfile.c
source/blender/editors/object/object_relations.c
source/blender/makesdna/DNA_ID.h
source/blender/makesrna/RNA_access.h
source/blender/makesrna/RNA_define.h
source/blender/makesrna/RNA_types.h
source/blender/makesrna/intern/rna_ID.c
source/blender/makesrna/intern/rna_access.c
source/blender/makesrna/intern/rna_define.c
source/blender/makesrna/intern/rna_internal.h
source/blender/makesrna/intern/rna_internal_types.h
source/blender/makesrna/intern/rna_ui.c
source/blender/makesrna/intern/rna_userdef.c
source/blender/makesrna/intern/rna_wm.c
source/blender/python/generic/idprop_py_api.c
source/blender/python/intern/bpy_props.c
source/blender/python/intern/bpy_props.h
source/blender/python/intern/bpy_rna.c
source/blender/python/intern/bpy_rna.h
source/blender/windowmanager/intern/wm.c
tests/python/CMakeLists.txt
tests/python/bl_pyapi_idprop_datablock.py [new file with mode: 0644]

index 5b10d7ebc068577ea24bf4cd197a8457b86c2a81..ab8728faedb64fadd31afe675187f0c4b2b0a710 100644 (file)
@@ -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
index 72ae2cf4efa9d7a309506acdad466a9f5d101279..6649cfbb5858b97fccea76da1d73c41a50b6c003 100644 (file)
@@ -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);
index 1258e2fa72e9899cce2bce326203da7ed5a266d9..68bf9f43fe1e7664b9e7d34acb7635768a1251be 100644 (file)
@@ -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);
index 5e4e8eb34ad93e4257e07653f2c3e21aeaaa0443..074a9a12fe087f1e50c5ca873f00112ab7441cc2 100644 (file)
@@ -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;
-       }
-}
-
 /** \} */
index 9685f1f5af6b04dcda279cade6d18a1d7fbd7662..0616c61484821ab9263e606309e5fd74442d7674 100644 (file)
@@ -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"
 #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;
                }
 
index b6f4621a0b3203336c4e5a19c3d6e86afc723567..d14e0cf0b65330829125b5276ef6d418bee1cc7a 100644 (file)
@@ -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);
index 3f3b48966535d9df6d29beff6d5cac333efcb29a..f3223e31b17ca28ba1b155a8835760a7155071ac 100644 (file)
@@ -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);
        }
 }
 
index 09c88ac945a91f57f92fd9bced0ef1406d2234de..c55b426c0259277f8f828bcd1621b8d8c9bc6797 100644 (file)
 #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, &lt->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);
index b5fbe4ba586724f0c3f75fe39b8f36aee090c74a..861e249b0eecdfb521a1c0ec53acc0ffb0290a13 100644 (file)
@@ -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);
 }
index 2c6f3d2fc6683bd4534811eb5f5db50349ba1adf..da0f505c4f38393f49bb049a105a7d37a3c06db4 100644 (file)
@@ -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,
index f9aaec69ce7f165e46ccf5d5ffb58b59037f6a93..a1af3f982744a7159a54dca0251c79a4c226b4dc 100644 (file)
@@ -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);
index 2a680b6eaebad6f8144c79ba189cd3888e2d46ca..6e62313b00af19c8e6a2c5d8a6ca07aa4a75df08 100644 (file)
@@ -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);
index dee8df7d9330d99760ebd5885288cab5d9ace18f..cd04f9e8a6d9aa22a18301fdd03c376faf4f1374 100644 (file)
@@ -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);
index 671902c5cc70c5b6741e43ec170776482c44292c..6ed2c55138cc87fb6a49fbf080048b282f187ca3 100644 (file)
@@ -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
index 7266cfb12d6345a0dff6d7380d48f4524f4a3aeb..c3d2d92fc5ede9d463152129f8dfceb831b2ffd8 100644 (file)
@@ -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));
                }
        }
 }
index 1d232d2df3941992acdba79f9a687dcec2b7deed..49983b0bc2b4ccf9533d3947703537fb7513a529 100644 (file)
@@ -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;
index 76455adbc78b5f6aa06463c981273a158d0699cd..dfd5af788f6877ecadc20e7f334585a693c9bdc8 100644 (file)
@@ -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;
index fce81e6967eff8448a691ccb83bce0b1fbac1f33..df591659fdb7f48e84f50a47bfe71de437775a4d 100644 (file)
@@ -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);
index 7a3c862f04c9f2373b54583f1fa96e9a421ed70a..54b82fc89d6df078ab76be20ada42908df1a721c 100644 (file)
@@ -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);
index 4d70b87843a58180f6f0f06c5b40df9830a5ec5d..d1e89ea18d0243b0d1c11ffb02e9f7188e601f07 100644 (file)
@@ -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);
index 35c9c9bcc891a58f1ec634741838550b57e0a64f..b458b2e69d538ea7107bb75fd8c8623870839483 100644 (file)
@@ -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)
index 0a9931f2683e4abf2beb2c586e62077cc140e109..5d6a7c578a214351264f9b83800c0f9aeaf8fef1 100644 (file)
@@ -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);
index f7348ac22501445ef234c1db3465148cf5f7341d..2656e612b18cc1c44ce8309daf229c4ff3a6077a 100644 (file)
 #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;
index c9934ca0cf3e745658394ed239309f72b6d937e8..614c1b4b708e56e1fb48d3092248a20cdd6064e8 100644 (file)
 
 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
index 2fd46ab94f04cf0d63e3fa91801983f4d2efe044..00627030db3c9190b070b0c66cd77a7422eb493e 100644 (file)
@@ -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) {
index e38d4f095d6534307a2cf53f23214372cb31377b..605f79b1ad81e12a8cca8e52b698ea4f830fdcb7 100644 (file)
@@ -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);
index 4351cd22b18e75e962c19bc76663eb8524a32e45..d0522fdd7d4c27d682d2672c5dcc5322716987ee 100644 (file)
@@ -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);
        }
 }
index f7ca9b0213764a18cffb7741afa51521c8d2859c..393aa512f0c6681040182d862207c64126edd46c 100644 (file)
@@ -91,6 +91,10 @@ add_test(script_pyapi_idprop ${TEST_BLENDER_EXE}
        --python ${CMAKE_CURRENT_LIST_DIR}/bl_pyapi_idprop.py
 )
 
+add_test(script_pyapi_idprop_datablock ${TEST_BLENDER_EXE}
+       --python ${CMAKE_CURRENT_LIST_DIR}/bl_pyapi_idprop_datablock.py
+)
+
 # ------------------------------------------------------------------------------
 # MODELING TESTS
 add_test(bevel ${TEST_BLENDER_EXE}
diff --git a/tests/python/bl_pyapi_idprop_datablock.py b/tests/python/bl_pyapi_idprop_datablock.py
new file mode 100644 (file)
index 0000000..4acfb83
--- /dev/null
@@ -0,0 +1,338 @@
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software Foundation,
+#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+import bpy
+import sys
+import os
+import tempfile
+import traceback
+import inspect
+from bpy.types import UIList
+
+arr_len = 100
+ob_cp_count = 100
+lib_path = os.path.join(tempfile.gettempdir(), "lib.blend")
+test_path = os.path.join(tempfile.gettempdir(), "test.blend")
+
+
+def print_fail_msg_and_exit(msg):
+    def __LINE__():
+        try:
+            raise Exception
+        except:
+            return sys.exc_info()[2].tb_frame.f_back.f_back.f_back.f_lineno
+
+    def __FILE__():
+        return inspect.currentframe().f_code.co_filename
+
+    print("'%s': %d >> %s" % (__FILE__(), __LINE__(), msg), file=sys.stderr)
+    sys.stderr.flush()
+    sys.stdout.flush()
+    os._exit(1)
+
+
+def abort_if_false(expr, msg=None):
+    if not expr:
+        if not msg:
+            msg = "test failed"
+        print_fail_msg_and_exit(msg)
+
+
+class TestClass(bpy.types.PropertyGroup):
+    test_prop = bpy.props.PointerProperty(type=bpy.types.Object)
+    name = bpy.props.StringProperty()
+
+
+def get_scene(lib_name, sce_name):
+    for s in bpy.data.scenes:
+        if s.name == sce_name:
+            if (s.library and s.library.name == lib_name) or \
+                    (lib_name == None and s.library == None):
+                return s
+
+
+def check_crash(fnc, args=None):
+    try:
+        fnc(args) if args else fnc()
+    except:
+        return
+    print_fail_msg_and_exit("test failed")
+
+
+def init():
+    bpy.utils.register_class(TestClass)
+    bpy.types.Object.prop_array = bpy.props.CollectionProperty(
+        name="prop_array",
+        type=TestClass)
+    bpy.types.Object.prop = bpy.props.PointerProperty(type=bpy.types.Object)
+
+
+def make_lib():
+    bpy.ops.wm.read_factory_settings()
+
+    # datablock pointer to the Camera object
+    bpy.data.objects["Cube"].prop = bpy.data.objects['Camera']
+
+    # array of datablock pointers to the Lamp object
+    for i in range(0, arr_len):
+        a = bpy.data.objects["Cube"].prop_array.add()
+        a.test_prop = bpy.data.objects['Lamp']
+        a.name = a.test_prop.name
+
+    # make unique named copy of the cube
+    ob = bpy.data.objects["Cube"].copy()
+    bpy.context.scene.objects.link(ob)
+
+    bpy.data.objects["Cube.001"].name = "Unique_Cube"
+
+    # duplicating of Cube
+    for i in range(0, ob_cp_count):
+        ob = bpy.data.objects["Cube"].copy()
+        bpy.context.scene.objects.link(ob)
+
+    # nodes
+    bpy.data.scenes["Scene"].use_nodes = True
+    bpy.data.scenes["Scene"].node_tree.nodes['Render Layers']["prop"] =\
+        bpy.data.objects['Camera']
+
+    # rename scene and save
+    bpy.data.scenes["Scene"].name = "Scene_lib"
+    bpy.ops.wm.save_as_mainfile(filepath=lib_path)
+
+
+def check_lib():
+    # check pointer
+    abort_if_false(bpy.data.objects["Cube"].prop == bpy.data.objects['Camera'])
+
+    # check array of pointers in duplicated object
+    for i in range(0, arr_len):
+        abort_if_false(bpy.data.objects["Cube.001"].prop_array[i].test_prop ==
+                       bpy.data.objects['Lamp'])
+
+
+def check_lib_linking():
+    # open startup file
+    bpy.ops.wm.read_factory_settings()
+
+    # link scene to the startup file
+    with bpy.data.libraries.load(lib_path, link=True) as (data_from, data_to):
+        data_to.scenes = ["Scene_lib"]
+
+    o = bpy.data.scenes["Scene_lib"].objects['Unique_Cube']
+
+    abort_if_false(o.prop_array[0].test_prop == bpy.data.scenes["Scene_lib"].objects['Lamp'])
+    abort_if_false(o.prop == bpy.data.scenes["Scene_lib"].objects['Camera'])
+    abort_if_false(o.prop.library == o.library)
+
+    bpy.ops.wm.save_as_mainfile(filepath=test_path)
+
+
+def check_linked_scene_copying():
+    # full copy of the scene with datablock props
+    bpy.ops.wm.open_mainfile(filepath=test_path)
+    bpy.data.screens['Default'].scene = bpy.data.scenes["Scene_lib"]
+    bpy.ops.scene.new(type='FULL_COPY')
+
+    # check save/open
+    bpy.ops.wm.save_as_mainfile(filepath=test_path)
+    bpy.ops.wm.open_mainfile(filepath=test_path)
+
+    intern_sce = get_scene(None, "Scene_lib")
+    extern_sce = get_scene("Lib", "Scene_lib")
+
+    # check node's props
+    # we made full copy from linked scene, so pointers must equal each other
+    abort_if_false(intern_sce.node_tree.nodes['Render Layers']["prop"] and
+                   intern_sce.node_tree.nodes['Render Layers']["prop"] ==
+                   extern_sce.node_tree.nodes['Render Layers']["prop"])
+
+
+def check_scene_copying():
+    # full copy of the scene with datablock props
+    bpy.ops.wm.open_mainfile(filepath=lib_path)
+    bpy.data.screens['Default'].scene = bpy.data.scenes["Scene_lib"]
+    bpy.ops.scene.new(type='FULL_COPY')
+
+    path = test_path + "_"
+    # check save/open
+    bpy.ops.wm.save_as_mainfile(filepath=path)
+    bpy.ops.wm.open_mainfile(filepath=path)
+
+    first_sce = get_scene(None, "Scene_lib")
+    second_sce = get_scene(None, "Scene_lib.001")
+
+    # check node's props
+    # must point to own scene camera
+    abort_if_false(not (first_sce.node_tree.nodes['Render Layers']["prop"] ==
+                        second_sce.node_tree.nodes['Render Layers']["prop"]))
+
+
+# count users
+def test_users_counting():
+    bpy.ops.wm.read_factory_settings()
+    lamp_us = bpy.data.objects["Lamp"].data.users
+    n = 1000
+    for i in range(0, n):
+        bpy.data.objects["Cube"]["a%s" % i] = bpy.data.objects["Lamp"].data
+    abort_if_false(bpy.data.objects["Lamp"].data.users == lamp_us + n)
+
+    for i in range(0, int(n / 2)):
+        bpy.data.objects["Cube"]["a%s" % i] = 1
+    abort_if_false(bpy.data.objects["Lamp"].data.users == lamp_us + int(n / 2))
+
+
+# linking
+def test_linking():
+    make_lib()
+    check_lib()
+    check_lib_linking()
+    check_linked_scene_copying()
+    check_scene_copying()
+
+
+# check restrictions for datablock pointers for some classes; GUI for manual testing
+def test_restrictions1():
+    class TEST_Op(bpy.types.Operator):
+        bl_idname = 'scene.test_op'
+        bl_label = 'Test'
+        bl_options = {"INTERNAL"}
+        str_prop = bpy.props.StringProperty(name="str_prop")
+
+        # disallow registration of datablock properties in operators
+        # will be checked in the draw method (test manually)
+        # also, see console:
+        #   ValueError: bpy_struct "SCENE_OT_test_op" doesn't support datablock properties
+        id_prop = bpy.props.PointerProperty(type=bpy.types.Object)
+
+        def execute(self, context):
+            return {'FINISHED'}
+
+    # just panel for testing the poll callback with lots of objects
+    class TEST_PT_DatablockProp(bpy.types.Panel):
+        bl_label = "Datablock IDProp"
+        bl_space_type = "PROPERTIES"
+        bl_region_type = "WINDOW"
+        bl_context = "render"
+
+        def draw(self, context):
+            self.layout.prop_search(context.scene, "prop", bpy.data,
+                                    "objects")
+            self.layout.template_ID(context.scene, "prop1")
+            self.layout.prop_search(context.scene, "prop2", bpy.data, "node_groups")
+
+            op = self.layout.operator("scene.test_op")
+            op.str_prop = "test string"
+
+            def test_fnc(op):
+                op["ob"] = bpy.data.objects['Unique_Cube']
+            check_crash(test_fnc, op)
+            abort_if_false(not hasattr(op, "id_prop"))
+
+    bpy.utils.register_class(TEST_PT_DatablockProp)
+    bpy.utils.register_class(TEST_Op)
+
+    def poll(self, value):
+        return value.name in bpy.data.scenes["Scene_lib"].objects
+
+    def poll1(self, value):
+        return True
+
+    bpy.types.Scene.prop = bpy.props.PointerProperty(type=bpy.types.Object)
+    bpy.types.Scene.prop1 = bpy.props.PointerProperty(type=bpy.types.Object, poll=poll)
+    bpy.types.Scene.prop2 = bpy.props.PointerProperty(type=bpy.types.NodeTree, poll=poll1)
+
+    # check poll effect on UI (poll returns false => red alert)
+    bpy.context.scene.prop = bpy.data.objects["Lamp.001"]
+    bpy.context.scene.prop1 = bpy.data.objects["Lamp.001"]
+
+    # check incorrect type assignment
+    def sub_test():
+        # NodeTree id_prop
+        bpy.context.scene.prop2 = bpy.data.objects["Lamp.001"]
+
+    check_crash(sub_test)
+
+    bpy.context.scene.prop2 = bpy.data.node_groups.new("Shader", "ShaderNodeTree")
+
+    print("Please, test GUI performance manually on the Render tab, '%s' panel" %
+          TEST_PT_DatablockProp.bl_label, file=sys.stderr)
+    sys.stderr.flush()
+
+
+# check some possible regressions
+def test_regressions():
+    bpy.types.Object.prop_str = bpy.props.StringProperty(name="str")
+    bpy.data.objects["Unique_Cube"].prop_str = "test"
+
+    bpy.types.Object.prop_gr = bpy.props.PointerProperty(
+        name="prop_gr",
+        type=TestClass,
+        description="test")
+
+    bpy.data.objects["Unique_Cube"].prop_gr = None
+
+
+# test restrictions for datablock pointers
+def test_restrictions2():
+    class TestClassCollection(bpy.types.PropertyGroup):
+        prop = bpy.props.CollectionProperty(
+            name="prop_array",
+            type=TestClass)
+    bpy.utils.register_class(TestClassCollection)
+
+    class TestPrefs(bpy.types.AddonPreferences):
+        bl_idname = "testprefs"
+        # expecting crash during registering
+        my_prop2 = bpy.props.PointerProperty(type=TestClass)
+
+        prop = bpy.props.PointerProperty(
+            name="prop",
+            type=TestClassCollection,
+            description="test")
+
+    bpy.types.Addon.a = bpy.props.PointerProperty(type=bpy.types.Object)
+
+    class TestUIList(UIList):
+        test = bpy.props.PointerProperty(type=bpy.types.Object)
+        def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
+            layout.prop(item, "name", text="", emboss=False, icon_value=icon)
+
+    check_crash(bpy.utils.register_class, TestPrefs)
+    check_crash(bpy.utils.register_class, TestUIList)
+
+    bpy.utils.unregister_class(TestClassCollection)
+
+
+def main():
+    init()
+    test_users_counting()
+    test_linking()
+    test_restrictions1()
+    check_crash(test_regressions)
+    test_restrictions2()
+
+
+if __name__ == "__main__":
+    try:
+        main()
+    except:
+        import traceback
+
+        traceback.print_exc()
+        sys.stderr.flush()
+        os._exit(1)