Datablock ID Properties

The absence of datablock properties "will certainly be resolved soon as the need for them is becoming obvious" said the [[http://wiki.blender.org/index.php/Dev:Ref/Release_Notes/2.67/Python_Nodes|Python Nodes release notes]]. So this patch allows Python scripts to create ID Properties which reference datablocks.
This functionality is implemented for `PointerProperty` and now such properties can be created with Python.

In addition to the standard update callback, `PointerProperty` can have a `poll` callback (standard RNA) which is useful for search menus. For details see the test included in this patch.

Original author: @artfunkel

Alexander (Blend4Web Team)

Reviewers: brecht, artfunkel, mont29, campbellbarton

Reviewed By: mont29, campbellbarton

Subscribers: jta, sergey, campbellbarton, wisaac, poseidon4o, mont29, homyachetser, Evgeny_Rodygin, AlexKowel, yurikovelenov, fjuhec, sharlybg, cardboard, duarteframos, blueprintrandom, a.romanov, BYOB, disnel, aditiapratama, bliblubli, dfelinto, lukastoenne

Maniphest Tasks: T37754

Differential Revision: https://developer.blender.org/D113
This commit is contained in:
Alexander Romanov 2017-04-13 12:30:03 +03:00
parent f90a243d9c
commit a7b3047cef
29 changed files with 983 additions and 204 deletions

View 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

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

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

View 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)
static IDProperty *IDP_CopyID(const IDProperty *prop)
{
if (prop->data.pointer)
id_us_min(((ID *)prop->data.pointer));
prop->data.pointer = id;
id_us_plus(id);
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;
}
void IDP_UnlinkID(IDProperty *prop)
{
id_us_min(((ID *)prop->data.pointer));
}
/** \} */
@ -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;
}
}
/** \} */

View 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"
@ -70,10 +71,12 @@
#include "BKE_animsys.h"
#include "BKE_constraint.h"
#include "BKE_fcurve.h"
#include "BKE_idprop.h"
#include "BKE_library.h"
#include "BKE_library_query.h"
#include "BKE_main.h"
#include "BKE_modifier.h"
#include "BKE_node.h"
#include "BKE_particle.h"
#include "BKE_rigidbody.h"
#include "BKE_sca.h"
@ -82,7 +85,9 @@
#define FOREACH_FINALIZE _finalize
#define FOREACH_FINALIZE_VOID FOREACH_FINALIZE: (void)0
#define FOREACH_FINALIZE_VOID \
if (0) { goto FOREACH_FINALIZE; } \
FOREACH_FINALIZE: ((void)0)
#define FOREACH_CALLBACK_INVOKE_ID_PP(_data, id_pp, _cb_flag) \
CHECK_TYPE(id_pp, ID **); \
@ -140,6 +145,37 @@ typedef struct LibraryForeachIDData {
BLI_LINKSTACK_DECLARE(ids_todo, ID *);
} LibraryForeachIDData;
static void library_foreach_idproperty_ID_link(LibraryForeachIDData *data, IDProperty *prop, int flag)
{
if (!prop)
return;
switch (prop->type) {
case IDP_GROUP:
{
for (IDProperty *loop = prop->data.group.first; loop; loop = loop->next) {
library_foreach_idproperty_ID_link(data, loop, flag);
}
break;
}
case IDP_IDPARRAY:
{
IDProperty *loop = IDP_Array(prop);
for (int i = 0; i < prop->len; i++) {
library_foreach_idproperty_ID_link(data, &loop[i], flag);
}
break;
}
case IDP_ID:
FOREACH_CALLBACK_INVOKE_ID(data, prop->data.pointer, flag);
break;
default:
break; /* Nothing to do here with other types of IDProperties... */
}
FOREACH_FINALIZE_VOID;
}
static void library_foreach_rigidbodyworldSceneLooper(
struct RigidBodyWorld *UNUSED(rbw), ID **id_pointer, void *user_data, int cb_flag)
{
@ -265,6 +301,17 @@ static void library_foreach_paint(LibraryForeachIDData *data, Paint *paint)
FOREACH_FINALIZE_VOID;
}
static void library_foreach_bone(LibraryForeachIDData *data, Bone *bone)
{
library_foreach_idproperty_ID_link(data, bone->prop, IDWALK_CB_USER);
for (Bone *curbone = bone->childbase.first; curbone; curbone = curbone->next) {
library_foreach_bone(data, curbone);
}
FOREACH_FINALIZE_VOID;
}
static void library_foreach_ID_as_subdata_link(
ID **id_pp, LibraryIDLinkCallback callback, void *user_data, int flag, LibraryForeachIDData *data)
{
@ -337,6 +384,8 @@ void BKE_library_foreach_ID_link(Main *bmain, ID *id, LibraryIDLinkCallback call
continue;
}
library_foreach_idproperty_ID_link(&data, id->properties, IDWALK_CB_USER);
AnimData *adt = BKE_animdata_from_id(id);
if (adt) {
library_foreach_animationData(&data, adt);
@ -402,6 +451,7 @@ void BKE_library_foreach_ID_link(Main *bmain, ID *id, LibraryIDLinkCallback call
CALLBACK_INVOKE(seq->clip, IDWALK_CB_USER);
CALLBACK_INVOKE(seq->mask, IDWALK_CB_USER);
CALLBACK_INVOKE(seq->sound, IDWALK_CB_USER);
library_foreach_idproperty_ID_link(&data, seq->prop, IDWALK_CB_USER);
for (SequenceModifierData *smd = seq->modifiers.first; smd; smd = smd->next) {
CALLBACK_INVOKE(smd->mask_id, IDWALK_CB_USER);
}
@ -515,6 +565,7 @@ void BKE_library_foreach_ID_link(Main *bmain, ID *id, LibraryIDLinkCallback call
data.cb_flag |= proxy_cb_flag;
for (pchan = object->pose->chanbase.first; pchan; pchan = pchan->next) {
library_foreach_idproperty_ID_link(&data, pchan->prop, IDWALK_CB_USER);
CALLBACK_INVOKE(pchan->custom, IDWALK_CB_USER);
BKE_constraints_id_loop(&pchan->constraints, library_foreach_constraintObjectLooper, &data);
}
@ -554,6 +605,16 @@ void BKE_library_foreach_ID_link(Main *bmain, ID *id, LibraryIDLinkCallback call
break;
}
case ID_AR:
{
bArmature *arm = (bArmature *)id;
for (Bone *bone = arm->bonebase.first; bone; bone = bone->next) {
library_foreach_bone(&data, bone);
}
break;
}
case ID_ME:
{
Mesh *mesh = (Mesh *) id;
@ -736,9 +797,27 @@ void BKE_library_foreach_ID_link(Main *bmain, ID *id, LibraryIDLinkCallback call
{
bNodeTree *ntree = (bNodeTree *) id;
bNode *node;
bNodeSocket *sock;
CALLBACK_INVOKE(ntree->gpd, IDWALK_CB_USER);
for (node = ntree->nodes.first; node; node = node->next) {
CALLBACK_INVOKE_ID(node->id, IDWALK_CB_USER);
library_foreach_idproperty_ID_link(&data, node->prop, IDWALK_CB_USER);
for (sock = node->inputs.first; sock; sock = sock->next) {
library_foreach_idproperty_ID_link(&data, sock->prop, IDWALK_CB_USER);
}
for (sock = node->outputs.first; sock; sock = sock->next) {
library_foreach_idproperty_ID_link(&data, sock->prop, IDWALK_CB_USER);
}
}
for (sock = ntree->inputs.first; sock; sock = sock->next) {
library_foreach_idproperty_ID_link(&data, sock->prop, IDWALK_CB_USER);
}
for (sock = ntree->outputs.first; sock; sock = sock->next) {
library_foreach_idproperty_ID_link(&data, sock->prop, IDWALK_CB_USER);
}
break;
}
@ -898,7 +977,6 @@ void BKE_library_foreach_ID_link(Main *bmain, ID *id, LibraryIDLinkCallback call
case ID_VF:
case ID_TXT:
case ID_SO:
case ID_AR:
case ID_GD:
case ID_WM:
case ID_PAL:
@ -948,9 +1026,25 @@ void BKE_library_update_ID_link_user(ID *id_dst, ID *id_src, const int cb_flag)
*/
/* XXX This has to be fully rethink, basing check on ID type is not really working anymore (and even worth once
* IDProps will support ID pointers), we'll have to do some quick checks on IDs themselves... */
bool BKE_library_idtype_can_use_idtype(const short id_type_owner, const short id_type_used)
bool BKE_library_id_can_use_idtype(ID *id_owner, const short id_type_used)
{
if (id_type_can_have_animdata(id_type_owner)) {
/* any type of ID can be used in custom props. */
if (id_owner->properties) {
return true;
}
const short id_type_owner = GS(id_owner->name);
/* IDProps of armature bones and nodes, and bNode->id can use virtually any type of ID. */
if (ELEM(id_type_owner, ID_NT, ID_AR)) {
return true;
}
if (ntreeFromID(id_owner)) {
return true;
}
if (BKE_animdata_from_id(id_owner)) {
return true; /* AnimationData can use virtually any kind of datablocks, through drivers especially. */
}
@ -959,8 +1053,7 @@ bool BKE_library_idtype_can_use_idtype(const short id_type_owner, const short id
return ELEM(id_type_used, ID_LI);
case ID_SCE:
return (ELEM(id_type_used, ID_OB, ID_WO, ID_SCE, ID_MC, ID_MA, ID_GR, ID_TXT,
ID_LS, ID_MSK, ID_SO, ID_GD, ID_BR, ID_PAL, ID_IM, ID_NT) ||
BKE_library_idtype_can_use_idtype(ID_NT, id_type_used));
ID_LS, ID_MSK, ID_SO, ID_GD, ID_BR, ID_PAL, ID_IM, ID_NT));
case ID_OB:
/* Could be the following, but simpler to just always say 'yes' here. */
#if 0
@ -977,13 +1070,13 @@ bool BKE_library_idtype_can_use_idtype(const short id_type_owner, const short id
case ID_MB:
return ELEM(id_type_used, ID_MA);
case ID_MA:
return (ELEM(id_type_used, ID_TE, ID_GR) || BKE_library_idtype_can_use_idtype(ID_NT, id_type_used));
return (ELEM(id_type_used, ID_TE, ID_GR));
case ID_TE:
return (ELEM(id_type_used, ID_IM, ID_OB) || BKE_library_idtype_can_use_idtype(ID_NT, id_type_used));
return (ELEM(id_type_used, ID_IM, ID_OB));
case ID_LT:
return ELEM(id_type_used, ID_KE);
case ID_LA:
return (ELEM(id_type_used, ID_TE) || BKE_library_idtype_can_use_idtype(ID_NT, id_type_used));
return (ELEM(id_type_used, ID_TE));
case ID_CA:
return ELEM(id_type_used, ID_OB);
case ID_KE:
@ -991,7 +1084,7 @@ bool BKE_library_idtype_can_use_idtype(const short id_type_owner, const short id
case ID_SCR:
return ELEM(id_type_used, ID_SCE);
case ID_WO:
return (ELEM(id_type_used, ID_TE) || BKE_library_idtype_can_use_idtype(ID_NT, id_type_used));
return (ELEM(id_type_used, ID_TE));
case ID_SPK:
return ELEM(id_type_used, ID_SO);
case ID_GR:
@ -1012,7 +1105,7 @@ bool BKE_library_idtype_can_use_idtype(const short id_type_owner, const short id
case ID_MSK:
return ELEM(id_type_used, ID_MC); /* WARNING! mask->parent.id, not typed. */
case ID_LS:
return (ELEM(id_type_used, ID_TE, ID_OB) || BKE_library_idtype_can_use_idtype(ID_NT, id_type_used));
return (ELEM(id_type_used, ID_TE, ID_OB));
case ID_IM:
case ID_VF:
case ID_TXT:
@ -1118,7 +1211,7 @@ static bool library_ID_is_used(Main *bmain, void *idv, const bool check_linked)
while (i-- && !is_defined) {
ID *id_curr = lb_array[i]->first;
if (!id_curr || !BKE_library_idtype_can_use_idtype(GS(id_curr->name), GS(id->name))) {
if (!id_curr || !BKE_library_id_can_use_idtype(id_curr, GS(id->name))) {
continue;
}
@ -1170,7 +1263,7 @@ void BKE_library_ID_test_usages(Main *bmain, void *idv, bool *is_used_local, boo
while (i-- && !is_defined) {
ID *id_curr = lb_array[i]->first;
if (!id_curr || !BKE_library_idtype_can_use_idtype(GS(id_curr->name), GS(id->name))) {
if (!id_curr || !BKE_library_id_can_use_idtype(id_curr, GS(id->name))) {
continue;
}

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

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

View File

@ -129,6 +129,7 @@
#include "BKE_library_idmap.h"
#include "BKE_library_query.h"
#include "BKE_idcode.h"
#include "BKE_idprop.h"
#include "BKE_material.h"
#include "BKE_main.h" // for Main
#include "BKE_mesh.h" // for ME_ defines (patching)
@ -2001,7 +2002,7 @@ static void test_pointer_array(FileData *fd, void **mat)
/* ************ READ ID Properties *************** */
static void IDP_DirectLinkProperty(IDProperty *prop, int switch_endian, FileData *fd);
static void IDP_LibLinkProperty(IDProperty *prop, int switch_endian, FileData *fd);
static void IDP_LibLinkProperty(IDProperty *prop, FileData *fd);
static void IDP_DirectLinkIDPArray(IDProperty *prop, int switch_endian, FileData *fd)
{
@ -2132,10 +2133,39 @@ static void _IDP_DirectLinkGroup_OrFree(IDProperty **prop, int switch_endian, Fi
}
}
/* stub function */
static void IDP_LibLinkProperty(IDProperty *UNUSED(prop), int UNUSED(switch_endian), FileData *UNUSED(fd))
static void IDP_LibLinkProperty(IDProperty *prop, FileData *fd)
{
/* Should we do something here, prop should be ensured to be non-NULL first... */
if (!prop)
return;
switch (prop->type) {
case IDP_ID: /* PointerProperty */
{
void *newaddr = newlibadr_us(fd, NULL, IDP_Id(prop));
if (IDP_Id(prop) && !newaddr && G.debug) {
printf("Error while loading \"%s\". Data not found in file!\n", prop->name);
}
prop->data.pointer = newaddr;
break;
}
case IDP_IDPARRAY: /* CollectionProperty */
{
IDProperty *idp_array = IDP_IDPArray(prop);
for (int i = 0; i < prop->len; i++) {
IDP_LibLinkProperty(&(idp_array[i]), fd);
}
break;
}
case IDP_GROUP: /* PointerProperty */
{
for (IDProperty *loop = prop->data.group.first; loop; loop = loop->next) {
IDP_LibLinkProperty(loop, fd);
}
break;
}
default:
break; /* Nothing to do for other IDProps. */
}
}
/* ************ READ IMAGE PREVIEW *************** */
@ -2195,7 +2225,7 @@ static void lib_link_brush(FileData *fd, Main *main)
/* only link ID pointers */
for (Brush *brush = main->brush.first; brush; brush = brush->id.next) {
if (brush->id.tag & LIB_TAG_NEED_LINK) {
IDP_LibLinkProperty(brush->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
IDP_LibLinkProperty(brush->id.properties, fd);
/* brush->(mask_)mtex.obj is ignored on purpose? */
brush->mtex.tex = newlibadr_us(fd, brush->id.lib, brush->mtex.tex);
@ -2232,7 +2262,7 @@ static void lib_link_palette(FileData *fd, Main *main)
/* only link ID pointers */
for (Palette *palette = main->palettes.first; palette; palette = palette->id.next) {
if (palette->id.tag & LIB_TAG_NEED_LINK) {
IDP_LibLinkProperty(palette->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
IDP_LibLinkProperty(palette->id.properties, fd);
palette->id.tag &= ~LIB_TAG_NEED_LINK;
}
@ -2250,7 +2280,7 @@ static void lib_link_paint_curve(FileData *fd, Main *main)
/* only link ID pointers */
for (PaintCurve *pc = main->paintcurves.first; pc; pc = pc->id.next) {
if (pc->id.tag & LIB_TAG_NEED_LINK) {
IDP_LibLinkProperty(pc->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
IDP_LibLinkProperty(pc->id.properties, fd);
pc->id.tag &= ~LIB_TAG_NEED_LINK;
}
@ -2505,7 +2535,7 @@ static void lib_link_action(FileData *fd, Main *main)
{
for (bAction *act = main->action.first; act; act = act->id.next) {
if (act->id.tag & LIB_TAG_NEED_LINK) {
IDP_LibLinkProperty(act->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
IDP_LibLinkProperty(act->id.properties, fd);
// XXX deprecated - old animation system <<<
for (bActionChannel *chan = act->chanbase.first; chan; chan = chan->next) {
@ -2712,7 +2742,7 @@ static void lib_link_cachefiles(FileData *fd, Main *bmain)
/* only link ID pointers */
for (CacheFile *cache_file = bmain->cachefiles.first; cache_file; cache_file = cache_file->id.next) {
if (cache_file->id.tag & LIB_TAG_NEED_LINK) {
IDP_LibLinkProperty(cache_file->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
IDP_LibLinkProperty(cache_file->id.properties, fd);
lib_link_animdata(fd, &cache_file->id, cache_file->adt);
cache_file->id.tag &= ~LIB_TAG_NEED_LINK;
@ -2746,20 +2776,13 @@ static void direct_link_motionpath(FileData *fd, bMotionPath *mpath)
/* ************ READ NODE TREE *************** */
static void lib_link_node_socket(FileData *fd, ID *UNUSED(id), bNodeSocket *sock)
{
/* Link ID Properties -- and copy this comment EXACTLY for easy finding
* of library blocks that implement this.*/
IDP_LibLinkProperty(sock->prop, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
}
/* Single node tree (also used for material/scene trees), ntree is not NULL */
static void lib_link_ntree(FileData *fd, ID *id, bNodeTree *ntree)
{
bNode *node;
bNodeSocket *sock;
IDP_LibLinkProperty(ntree->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
IDP_LibLinkProperty(ntree->id.properties, fd);
lib_link_animdata(fd, &ntree->id, ntree->adt);
ntree->gpd = newlibadr_us(fd, id->lib, ntree->gpd);
@ -2767,27 +2790,23 @@ static void lib_link_ntree(FileData *fd, ID *id, bNodeTree *ntree)
for (node = ntree->nodes.first; node; node = node->next) {
/* Link ID Properties -- and copy this comment EXACTLY for easy finding
* of library blocks that implement this.*/
IDP_LibLinkProperty(node->prop, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
IDP_LibLinkProperty(node->prop, fd);
node->id = newlibadr_us(fd, id->lib, node->id);
for (sock = node->inputs.first; sock; sock = sock->next) {
IDP_LibLinkProperty(sock->prop, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
lib_link_node_socket(fd, id, sock);
IDP_LibLinkProperty(sock->prop, fd);
}
for (sock = node->outputs.first; sock; sock = sock->next) {
IDP_LibLinkProperty(sock->prop, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
lib_link_node_socket(fd, id, sock);
IDP_LibLinkProperty(sock->prop, fd);
}
}
for (sock = ntree->inputs.first; sock; sock = sock->next) {
IDP_LibLinkProperty(sock->prop, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
lib_link_node_socket(fd, id, sock);
IDP_LibLinkProperty(sock->prop, fd);
}
for (sock = ntree->outputs.first; sock; sock = sock->next) {
IDP_LibLinkProperty(sock->prop, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
lib_link_node_socket(fd, id, sock);
IDP_LibLinkProperty(sock->prop, fd);
}
}
@ -3292,6 +3311,8 @@ static void lib_link_pose(FileData *fd, Main *bmain, Object *ob, bPose *pose)
pchan->bone = BLI_ghash_lookup(bone_hash, pchan->name);
IDP_LibLinkProperty(pchan->prop, fd);
pchan->custom = newlibadr_us(fd, arm->id.lib, pchan->custom);
if (UNLIKELY(pchan->bone == NULL)) {
rebuild = true;
@ -3312,13 +3333,26 @@ static void lib_link_pose(FileData *fd, Main *bmain, Object *ob, bPose *pose)
}
}
static void lib_link_bones(FileData *fd, Bone *bone)
{
IDP_LibLinkProperty(bone->prop, fd);
for (Bone *curbone = bone->childbase.first; curbone; curbone = curbone->next) {
lib_link_bones(fd, curbone);
}
}
static void lib_link_armature(FileData *fd, Main *main)
{
for (bArmature *arm = main->armature.first; arm; arm = arm->id.next) {
if (arm->id.tag & LIB_TAG_NEED_LINK) {
IDP_LibLinkProperty(arm->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
IDP_LibLinkProperty(arm->id.properties, fd);
lib_link_animdata(fd, &arm->id, arm->adt);
for (Bone *curbone = arm->bonebase.first; curbone; curbone = curbone->next) {
lib_link_bones(fd, curbone);
}
arm->id.tag &= ~LIB_TAG_NEED_LINK;
}
}
@ -3365,7 +3399,7 @@ static void lib_link_camera(FileData *fd, Main *main)
{
for (Camera *ca = main->camera.first; ca; ca = ca->id.next) {
if (ca->id.tag & LIB_TAG_NEED_LINK) {
IDP_LibLinkProperty(ca->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
IDP_LibLinkProperty(ca->id.properties, fd);
lib_link_animdata(fd, &ca->id, ca->adt);
ca->ipo = newlibadr_us(fd, ca->id.lib, ca->ipo); // XXX deprecated - old animation system
@ -3390,7 +3424,7 @@ static void lib_link_lamp(FileData *fd, Main *main)
{
for (Lamp *la = main->lamp.first; la; la = la->id.next) {
if (la->id.tag & LIB_TAG_NEED_LINK) {
IDP_LibLinkProperty(la->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
IDP_LibLinkProperty(la->id.properties, fd);
lib_link_animdata(fd, &la->id, la->adt);
for (int a = 0; a < MAX_MTEX; a++) {
@ -3455,7 +3489,7 @@ static void lib_link_key(FileData *fd, Main *main)
BLI_assert((key->id.tag & LIB_TAG_EXTERN) == 0);
if (key->id.tag & LIB_TAG_NEED_LINK) {
IDP_LibLinkProperty(key->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
IDP_LibLinkProperty(key->id.properties, fd);
lib_link_animdata(fd, &key->id, key->adt);
key->ipo = newlibadr_us(fd, key->id.lib, key->ipo); // XXX deprecated - old animation system
@ -3520,7 +3554,7 @@ static void lib_link_mball(FileData *fd, Main *main)
{
for (MetaBall *mb = main->mball.first; mb; mb = mb->id.next) {
if (mb->id.tag & LIB_TAG_NEED_LINK) {
IDP_LibLinkProperty(mb->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
IDP_LibLinkProperty(mb->id.properties, fd);
lib_link_animdata(fd, &mb->id, mb->adt);
for (int a = 0; a < mb->totcol; a++) {
@ -3556,7 +3590,7 @@ static void lib_link_world(FileData *fd, Main *main)
{
for (World *wrld = main->world.first; wrld; wrld = wrld->id.next) {
if (wrld->id.tag & LIB_TAG_NEED_LINK) {
IDP_LibLinkProperty(wrld->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
IDP_LibLinkProperty(wrld->id.properties, fd);
lib_link_animdata(fd, &wrld->id, wrld->adt);
wrld->ipo = newlibadr_us(fd, wrld->id.lib, wrld->ipo); // XXX deprecated - old animation system
@ -3607,7 +3641,7 @@ static void lib_link_vfont(FileData *fd, Main *main)
{
for (VFont *vf = main->vfont.first; vf; vf = vf->id.next) {
if (vf->id.tag & LIB_TAG_NEED_LINK) {
IDP_LibLinkProperty(vf->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
IDP_LibLinkProperty(vf->id.properties, fd);
vf->id.tag &= ~LIB_TAG_NEED_LINK;
}
@ -3627,7 +3661,7 @@ static void lib_link_text(FileData *fd, Main *main)
{
for (Text *text = main->text.first; text; text = text->id.next) {
if (text->id.tag & LIB_TAG_NEED_LINK) {
IDP_LibLinkProperty(text->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
IDP_LibLinkProperty(text->id.properties, fd);
text->id.tag &= ~LIB_TAG_NEED_LINK;
}
@ -3679,7 +3713,7 @@ static void lib_link_image(FileData *fd, Main *main)
{
for (Image *ima = main->image.first; ima; ima = ima->id.next) {
if (ima->id.tag & LIB_TAG_NEED_LINK) {
IDP_LibLinkProperty(ima->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
IDP_LibLinkProperty(ima->id.properties, fd);
ima->id.tag &= ~LIB_TAG_NEED_LINK;
}
@ -3746,7 +3780,7 @@ static void lib_link_curve(FileData *fd, Main *main)
{
for (Curve *cu = main->curve.first; cu; cu = cu->id.next) {
if (cu->id.tag & LIB_TAG_NEED_LINK) {
IDP_LibLinkProperty(cu->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
IDP_LibLinkProperty(cu->id.properties, fd);
lib_link_animdata(fd, &cu->id, cu->adt);
for (int a = 0; a < cu->totcol; a++) {
@ -3838,7 +3872,7 @@ static void lib_link_texture(FileData *fd, Main *main)
{
for (Tex *tex = main->tex.first; tex; tex = tex->id.next) {
if (tex->id.tag & LIB_TAG_NEED_LINK) {
IDP_LibLinkProperty(tex->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
IDP_LibLinkProperty(tex->id.properties, fd);
lib_link_animdata(fd, &tex->id, tex->adt);
tex->ima = newlibadr_us(fd, tex->id.lib, tex->ima);
@ -3916,12 +3950,12 @@ static void lib_link_material(FileData *fd, Main *main)
{
for (Material *ma = main->mat.first; ma; ma = ma->id.next) {
if (ma->id.tag & LIB_TAG_NEED_LINK) {
IDP_LibLinkProperty(ma->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
IDP_LibLinkProperty(ma->id.properties, fd);
lib_link_animdata(fd, &ma->id, ma->adt);
/* Link ID Properties -- and copy this comment EXACTLY for easy finding
* of library blocks that implement this.*/
IDP_LibLinkProperty(ma->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
IDP_LibLinkProperty(ma->id.properties, fd);
ma->ipo = newlibadr_us(fd, ma->id.lib, ma->ipo); // XXX deprecated - old animation system
ma->group = newlibadr_us(fd, ma->id.lib, ma->group);
@ -4060,7 +4094,7 @@ static void lib_link_particlesettings(FileData *fd, Main *main)
{
for (ParticleSettings *part = main->particle.first; part; part = part->id.next) {
if (part->id.tag & LIB_TAG_NEED_LINK) {
IDP_LibLinkProperty(part->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
IDP_LibLinkProperty(part->id.properties, fd);
lib_link_animdata(fd, &part->id, part->adt);
part->ipo = newlibadr_us(fd, part->id.lib, part->ipo); // XXX deprecated - old animation system
@ -4382,7 +4416,7 @@ static void lib_link_mesh(FileData *fd, Main *main)
/* Link ID Properties -- and copy this comment EXACTLY for easy finding
* of library blocks that implement this.*/
IDP_LibLinkProperty(me->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
IDP_LibLinkProperty(me->id.properties, fd);
lib_link_animdata(fd, &me->id, me->adt);
/* this check added for python created meshes */
@ -4657,7 +4691,7 @@ static void lib_link_latt(FileData *fd, Main *main)
{
for (Lattice *lt = main->latt.first; lt; lt = lt->id.next) {
if (lt->id.tag & LIB_TAG_NEED_LINK) {
IDP_LibLinkProperty(lt->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
IDP_LibLinkProperty(lt->id.properties, fd);
lib_link_animdata(fd, &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);

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

View 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,

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

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

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

View 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

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

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

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

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

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

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

View 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)

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

View File

@ -50,11 +50,13 @@
#include "../generic/py_capi_utils.h"
/* initial definition of callback slots we'll probably have more than 1 */
#define BPY_DATA_CB_SLOT_SIZE 3
#define BPY_DATA_CB_SLOT_UPDATE 0
#define BPY_DATA_CB_SLOT_GET 1
#define BPY_DATA_CB_SLOT_SET 2
enum {
BPY_DATA_CB_SLOT_UPDATE = 0,
BPY_DATA_CB_SLOT_GET = 1,
BPY_DATA_CB_SLOT_SET = 2,
BPY_DATA_CB_SLOT_POLL = 3,
BPY_DATA_CB_SLOT_SIZE = 4,
};
extern BPy_StructRNA *bpy_context_module;
@ -71,6 +73,9 @@ static EnumPropertyItem property_flag_items[] = {
" :arg options: Enumerator in ['HIDDEN', 'SKIP_SAVE', 'ANIMATABLE', 'LIBRARY_EDITABLE', 'PROPORTIONAL'," \
"'TEXTEDIT_UPDATE'].\n" \
" :type options: set\n" \
" :arg poll: function to be called to determine whether an item is valid for this property.\n" \
" The function must take 2 values (self,object) and return Bool.\n" \
" :type poll: function\n" \
static EnumPropertyItem property_flag_enum_items[] = {
{PROP_HIDDEN, "HIDDEN", 0, "Hidden", ""},
@ -389,6 +394,51 @@ static void bpy_prop_boolean_set_cb(struct PointerRNA *ptr, struct PropertyRNA *
}
}
static int bpy_prop_poll_cb(struct PointerRNA *self, PointerRNA candidate, struct PropertyRNA *prop)
{
PyObject *py_self;
PyObject *py_candidate;
PyObject *py_func;
PyObject **py_data = RNA_property_py_data_get(prop);
PyObject *args;
PyObject *ret;
bool result;
const int is_write_ok = pyrna_write_check();
PyGILState_STATE gilstate = PyGILState_Ensure();
BLI_assert(self != NULL);
py_self = pyrna_struct_as_instance(self);
py_candidate = pyrna_struct_as_instance(&candidate);
py_func = py_data[BPY_DATA_CB_SLOT_POLL];
if (!is_write_ok)
pyrna_write_set(true);
args = PyTuple_New(2);
PyTuple_SET_ITEM(args, 0, py_self);
PyTuple_SET_ITEM(args, 1, py_candidate);
ret = PyObject_CallObject(py_func, args);
Py_DECREF(args);
if (ret == NULL) {
printf_func_error(py_func);
result = false;
}
else {
result = PyObject_IsTrue(ret);
Py_DECREF(ret);
}
PyGILState_Release(gilstate);
if (!is_write_ok)
pyrna_write_set(false);
return result;
}
static void bpy_prop_boolean_array_get_cb(struct PointerRNA *ptr, struct PropertyRNA *prop, int *values)
{
PyObject **py_data = RNA_property_py_data_get(prop);
@ -1598,6 +1648,16 @@ static void bpy_prop_callback_assign_update(struct PropertyRNA *prop, PyObject *
}
}
static void bpy_prop_callback_assign_pointer(struct PropertyRNA *prop, PyObject *poll_cb)
{
if (poll_cb && poll_cb != Py_None) {
PyObject **py_data = bpy_prop_py_data_get(prop);
RNA_def_property_poll_runtime(prop, (void *) bpy_prop_poll_cb);
py_data[BPY_DATA_CB_SLOT_POLL] = poll_cb;
}
}
static void bpy_prop_callback_assign_boolean(struct PropertyRNA *prop, PyObject *get_cb, PyObject *set_cb)
{
BooleanPropertyGetFunc rna_get_cb = NULL;
@ -1904,7 +1964,7 @@ static void bpy_prop_callback_assign_enum(struct PropertyRNA *prop, PyObject *ge
" :type set: function\n" \
#define BPY_PROPDEF_TYPE_DOC \
" :arg type: A subclass of :class:`bpy.types.PropertyGroup`.\n" \
" :arg type: A subclass of :class:`bpy.types.PropertyGroup` or :class:`bpy.types.ID`.\n" \
" :type type: class\n" \
#if 0
@ -2772,7 +2832,7 @@ static PyObject *BPy_EnumProperty(PyObject *self, PyObject *args, PyObject *kw)
Py_RETURN_NONE;
}
static StructRNA *pointer_type_from_py(PyObject *value, const char *error_prefix)
StructRNA *pointer_type_from_py(PyObject *value, const char *error_prefix)
{
StructRNA *srna;
@ -2782,25 +2842,18 @@ static StructRNA *pointer_type_from_py(PyObject *value, const char *error_prefix
PyObject *msg = PyC_ExceptionBuffer();
const char *msg_char = _PyUnicode_AsString(msg);
PyErr_Format(PyExc_TypeError,
"%.200s expected an RNA type derived from PropertyGroup, failed with: %s",
"%.200s expected an RNA type, failed with: %s",
error_prefix, msg_char);
Py_DECREF(msg);
}
else {
PyErr_Format(PyExc_TypeError,
"%.200s expected an RNA type derived from PropertyGroup, failed with type '%s'",
"%.200s expected an RNA type, failed with type '%s'",
error_prefix, Py_TYPE(value)->tp_name);
}
return NULL;
}
if (!RNA_struct_is_a(srna, &RNA_PropertyGroup)) {
PyErr_Format(PyExc_TypeError,
"%.200s expected an RNA type derived from PropertyGroup",
error_prefix);
return NULL;
}
return srna;
}
@ -2809,7 +2862,8 @@ PyDoc_STRVAR(BPy_PointerProperty_doc,
"name=\"\", "
"description=\"\", "
"options={'ANIMATABLE'}, "
"update=None)\n"
"update=None,\n"
"poll=None)\n"
"\n"
" Returns a new pointer property definition.\n"
"\n"
@ -2819,14 +2873,14 @@ BPY_PROPDEF_DESC_DOC
BPY_PROPDEF_OPTIONS_DOC
BPY_PROPDEF_UPDATE_DOC
);
static PyObject *BPy_PointerProperty(PyObject *self, PyObject *args, PyObject *kw)
PyObject *BPy_PointerProperty(PyObject *self, PyObject *args, PyObject *kw)
{
StructRNA *srna;
BPY_PROPDEF_HEAD(PointerProperty);
if (srna) {
static const char *kwlist[] = {"attr", "type", "name", "description", "options", "update", NULL};
static const char *kwlist[] = {"attr", "type", "name", "description", "options", "poll", "update", NULL};
const char *id = NULL, *name = NULL, *description = "";
int id_len;
PropertyRNA *prop;
@ -2834,33 +2888,47 @@ static PyObject *BPy_PointerProperty(PyObject *self, PyObject *args, PyObject *k
PyObject *type = Py_None;
PyObject *pyopts = NULL;
int opts = 0;
PyObject *update_cb = NULL;
PyObject *update_cb = NULL, *poll_cb = NULL;
if (!PyArg_ParseTupleAndKeywords(args, kw,
"s#O|ssO!O:PointerProperty",
"s#O|ssO!OOO:PointerProperty",
(char **)kwlist, &id, &id_len,
&type, &name, &description,
&PySet_Type, &pyopts,
&update_cb))
&poll_cb, &update_cb))
{
return NULL;
}
BPY_PROPDEF_CHECK(PointerProperty, property_flag_items);
ptype = pointer_type_from_py(type, "PointerProperty(...):");
ptype = pointer_type_from_py(type, "PointerProperty(...)");
if (!ptype)
return NULL;
if (!RNA_struct_is_a(ptype, &RNA_PropertyGroup) && !RNA_struct_is_ID(ptype)) {
PyErr_Format(PyExc_TypeError,
"PointerProperty(...) expected an RNA type derived from %.200s or %.200s",
RNA_struct_ui_name(&RNA_ID), RNA_struct_ui_name(&RNA_PropertyGroup));
return NULL;
}
if (bpy_prop_callback_check(update_cb, "update", 2) == -1) {
return NULL;
}
if (bpy_prop_callback_check(poll_cb, "poll", 2) == -1) {
return NULL;
}
prop = RNA_def_pointer_runtime(srna, id, ptype, name ? name : id, description);
if (pyopts) {
bpy_prop_assign_flag(prop, opts);
}
if (RNA_struct_idprops_contains_datablock(ptype)) {
if (RNA_struct_is_a(srna, &RNA_PropertyGroup)) {
RNA_def_struct_flag(srna, STRUCT_CONTAINS_DATABLOCK_IDPROPERTIES);
}
}
bpy_prop_callback_assign_update(prop, update_cb);
bpy_prop_callback_assign_pointer(prop, poll_cb);
RNA_def_property_duplicate_pointers(srna, prop);
}
Py_RETURN_NONE;
@ -2879,7 +2947,7 @@ BPY_PROPDEF_NAME_DOC
BPY_PROPDEF_DESC_DOC
BPY_PROPDEF_OPTIONS_DOC
);
static PyObject *BPy_CollectionProperty(PyObject *self, PyObject *args, PyObject *kw)
PyObject *BPy_CollectionProperty(PyObject *self, PyObject *args, PyObject *kw)
{
StructRNA *srna;
@ -2910,10 +2978,23 @@ static PyObject *BPy_CollectionProperty(PyObject *self, PyObject *args, PyObject
if (!ptype)
return NULL;
if (!RNA_struct_is_a(ptype, &RNA_PropertyGroup)) {
PyErr_Format(PyExc_TypeError,
"CollectionProperty(...) expected an RNA type derived from %.200s",
RNA_struct_ui_name(&RNA_ID), RNA_struct_ui_name(&RNA_PropertyGroup));
return NULL;
}
prop = RNA_def_collection_runtime(srna, id, ptype, name ? name : id, description);
if (pyopts) {
bpy_prop_assign_flag(prop, opts);
}
if (RNA_struct_idprops_contains_datablock(ptype)) {
if (RNA_struct_is_a(srna, &RNA_PropertyGroup)) {
RNA_def_struct_flag(srna, STRUCT_CONTAINS_DATABLOCK_IDPROPERTIES);
}
}
RNA_def_property_duplicate_pointers(srna, prop);
}
Py_RETURN_NONE;

View File

@ -30,6 +30,10 @@
PyObject *BPY_rna_props(void);
PyObject *BPy_PointerProperty(PyObject *self, PyObject *args, PyObject *kw);
PyObject *BPy_CollectionProperty(PyObject *self, PyObject *args, PyObject *kw);
StructRNA *pointer_type_from_py(PyObject *value, const char *error_prefix);
#define PYRNA_STACK_ARRAY 32
#endif

View 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) {

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

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

View 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}

View File

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