1
1

Compare commits

...

33 Commits

Author SHA1 Message Date
78f1476d93 Some more cleanup, get rid of hack to protect against NULL RNA pointerprops. 2017-04-04 16:13:55 +02:00
5937ebba44 Merge branch 'master' into datablock_idprops 2017-04-04 16:04:35 +02:00
e74254dd48 Some minor cleanup. 2017-04-04 15:41:39 +02:00
e131783384 Merge branch 'master' into datablock_idprops 2017-04-04 10:38:21 +02:00
6347b0b0aa Some cleanup regarding STRUCT_CONTAINS_DATABLOCK_IDPROPERTIES.
This whole system (how to prevent IDP_ID-forbidden structs to get
assigned IDP_ID-allowed IDProps) still seems somewhat brittle to me,
took me some time to wrap my head around it, but... could not find
any better way to do that so far.
2017-04-04 10:29:14 +02:00
9cf2a581ab Merge branch 'master' into datablock_idprops 2017-04-02 18:08:48 +02:00
d424c8041d Cleanup: Some minor styling. 2017-03-31 23:56:18 +02:00
3c76da79b4 Merge branch 'master' into datablock_idprops 2017-03-31 19:41:09 +02:00
4a3708b7ea Add missing handling of sequencer's strips IDProperties. 2017-03-31 19:40:12 +02:00
26549b5ba2 Cleanup: simpler to define 'no datablock idprop' flag in RNA struct definitions.
That's cleaner & easier to read than to do it for every child class in
register functions...
2017-03-31 15:27:56 +02:00
39e1121698 Merge branch 'master' into datablock_idprops 2017-03-31 12:19:30 +02:00
aebd8a7328 More cleanup anf fixes! 2017-03-31 10:57:01 +02:00
8072d4bd66 Merge branch 'master' into datablock_idprops 2017-03-31 10:44:55 +02:00
952f31b0d8 Fix bunch of missing/incorrect handling of IDProps.
Mainly in readfile.c and library_query.c.
Plus some other minor fixes and cleanup.
2017-03-31 10:42:45 +02:00
01e0b38b66 Grr, fix stupid typo in previous commit...
Always build before committing :|
2017-03-30 23:06:38 +02:00
314ccf6494 Merge branch 'master' into datablock_idprops 2017-03-30 23:00:12 +02:00
abb876d84b Cleanup - resync with master, this needs to be fixed in master first! 2017-03-30 21:19:17 +02:00
b7464ec6a2 Fix another case of bad logic in recurrent function handling IDProps.
And some generic cleanup/styling/etc.
2017-03-30 21:18:04 +02:00
e444a41545 Merge branch 'master' into datablock_idprops 2017-03-30 12:56:31 +02:00
e446935265 Fix always-unlinking in ID free function.
Since IDProps now handle ID usages, makes sense to pass do_id_user flag
to some new IDP_FreeProperty_ex() as well...
2017-03-30 12:52:07 +02:00
171c39cc19 Merge branch 'master' into datablock_idprops 2017-03-29 16:25:09 +02:00
aae70f182b Cleanup, minor fixes and serious simplification of idprops.c
Mostly, get rid of id_(un)register, we can just use mere id_us_plus/min
as anywhere else in code now. Also, unlink function was not actually
used.
2017-03-29 16:21:40 +02:00
603aafc9dc Fix missing handling of IDProps of nodetree's IO sockets. 2017-03-29 16:19:00 +02:00
93543e6695 Fix mistake with last master merge... 2017-03-29 08:41:42 +02:00
f512c8c2a8 Merge branch 'master' into datablock_idprops 2017-03-28 15:51:14 +02:00
a5f316e999 Merge branch 'master' into datablock_idprops
Conflicts:
	source/blender/blenloader/intern/readfile.c
2017-03-28 14:35:18 +02:00
43b255a2a1 Cleanup: remove 'generic' ID liblink function in readfile.c
We do need some generic ID handling refactor here (as was recently done
for writefile.c), but this out of scope of this patch - and not the way
to do it.
2017-03-27 16:50:15 +02:00
eef52b2818 Merge branch 'master' into datablock_idprops 2017-03-27 15:19:50 +02:00
60ac61b13f Fix obvious mistake in logic of two functions recursively handling IDProps.
They were expecting IDP_group as parameter, but then recursiveley
calling themselves with IDProps from groups and arrays, which can be of
any type...
2017-03-27 15:17:29 +02:00
38589ce6ef Fix bad auto-generated UI for ID IDProps.
`RNA_path_resolve()` tries to 'dereference' pointer properties, was not
a problem before but now that we do have pointer IDProps we want to get
property here, not it's pointed data.
2017-03-27 14:52:15 +02:00
95e0cb499b Cleanup and minor changes.
No functional change expected, mostly:
* Some renaming, bit of style editing, cleanup...
* Removing some useless diff from master.
2017-03-27 14:49:40 +02:00
db0bada1c3 Merge branch 'master' into datablock_idprops 2017-03-27 10:48:21 +02:00
a3f48d65df Datablock ID Properties
Summary:
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

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

Maniphest Tasks: T37754

Differential Revision: https://developer.blender.org/D113
2017-03-21 17:23:26 +03:00
34 changed files with 989 additions and 214 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));
}
/** \} */
@@ -723,13 +725,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);
}
@@ -746,12 +748,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
@@ -848,6 +889,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);
@@ -921,6 +964,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:
@@ -967,6 +1011,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");
@@ -981,11 +1033,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:
@@ -995,14 +1046,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);
@@ -1010,18 +1071,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"
@@ -140,6 +143,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 +299,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 +382,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 +449,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 +563,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 +603,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 +795,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 +975,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 +1024,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 +1051,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 +1068,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 +1082,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 +1103,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 +1209,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 +1261,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

@@ -695,7 +695,7 @@ void UI_but_func_search_set(
int UI_searchbox_size_y(void);
int UI_searchbox_size_x(void);
/* check if a string is in an existing search box */
int UI_search_items_find_index(uiSearchItems *items, const char *name);
int UI_search_items_find_index(uiSearchItems *items, const char *name, const size_t offset);
void UI_block_func_handle_set(uiBlock *block, uiBlockHandleFunc func, void *arg);
void UI_block_func_butmenu_set(uiBlock *block, uiMenuHandleFunc func, void *arg);

View File

@@ -4372,7 +4372,7 @@ void UI_but_func_search_set(
if (0 == (but->block->flag & UI_BLOCK_LOOP)) {
/* skip empty buttons, not all buttons need input, we only show invalid */
if (but->drawstr[0])
ui_but_search_refresh(but);
ui_but_search_refresh(but, false);
}
}

View File

@@ -605,7 +605,7 @@ int ui_searchbox_autocomplete(struct bContext *C, struct ARegion *ar, uiBut *but
void ui_searchbox_event(struct bContext *C, struct ARegion *ar, uiBut *but, const struct wmEvent *event);
bool ui_searchbox_apply(uiBut *but, struct ARegion *ar);
void ui_searchbox_free(struct bContext *C, struct ARegion *ar);
void ui_but_search_refresh(uiBut *but);
void ui_but_search_refresh(uiBut *but, const bool is_template_ID);
uiBlock *ui_popup_block_refresh(
struct bContext *C, uiPopupBlockHandle *handle,

View File

@@ -811,11 +811,11 @@ int UI_searchbox_size_x(void)
return 12 * UI_UNIT_X;
}
int UI_search_items_find_index(uiSearchItems *items, const char *name)
int UI_search_items_find_index(uiSearchItems *items, const char *name, const size_t offset)
{
int i;
for (i = 0; i < items->totitem; i++) {
if (STREQ(name, items->names[i])) {
if (STREQ(name, items->names[i] + offset)) {
return i;
}
}
@@ -894,7 +894,7 @@ static void ui_searchbox_butrect(rcti *r_rect, uiSearchboxData *data, int itemnr
int ui_searchbox_find_index(ARegion *ar, const char *name)
{
uiSearchboxData *data = ar->regiondata;
return UI_search_items_find_index(&data->items, name);
return UI_search_items_find_index(&data->items, name, 0);
}
/* x and y in screencoords */
@@ -1420,14 +1420,14 @@ void ui_searchbox_free(bContext *C, ARegion *ar)
/* sets red alert if button holds a string it can't find */
/* XXX weak: search_func adds all partial matches... */
void ui_but_search_refresh(uiBut *but)
void ui_but_search_refresh(uiBut *but, const bool is_template_ID)
{
uiSearchItems *items;
int x1;
/* possibly very large lists (such as ID datablocks) only
* only validate string RNA buts (not pointers) */
if (but->rnaprop && RNA_property_type(but->rnaprop) != PROP_STRING) {
/* possibly very large lists (such as ID datablocks),
* only validate string and pointer RNA buts */
if (but->rnaprop && !ELEM(RNA_property_type(but->rnaprop), PROP_STRING, PROP_POINTER)) {
return;
}
@@ -1447,7 +1447,8 @@ void ui_but_search_refresh(uiBut *but)
UI_but_flag_enable(but, UI_BUT_REDALERT);
}
else if (items->more == 0) {
if (UI_search_items_find_index(items, but->drawstr) == -1) {
const size_t offset = is_template_ID ? 3 : 0;
if (UI_search_items_find_index(items, but->drawstr, offset) == -1) {
UI_but_flag_enable(but, UI_BUT_REDALERT);
}
}

View File

@@ -451,6 +451,11 @@ static void template_ID(
but = uiDefButR(block, UI_BTYPE_TEXT, 0, name, 0, 0, UI_UNIT_X * 6, UI_UNIT_Y,
&idptr, "name", -1, 0, 0, -1, -1, RNA_struct_ui_description(type));
UI_but_funcN_set(but, template_id_cb, MEM_dupallocN(template), SET_INT_IN_POINTER(UI_ID_RENAME));
but->search_func = id_search_cb;
but->search_arg = template;
ui_but_search_refresh(but, true);
if (user_alert) UI_but_flag_enable(but, UI_BUT_REDALERT);
if (id->lib) {

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,35 @@ void ED_object_single_users(Main *bmain, Scene *scene, const bool full, const bo
single_tex_users_expand(bmain);
}
/* Relink datablock pointer properties */
{
Base *base;
Object *ob;
IDP_RelinkProperty(scene->id.properties);
for (base = scene->base.first; base; base = base->next) {
ob = base->object;
if (!ID_IS_LINKED_DATABLOCK(ob)) {
IDP_RelinkProperty(ob->id.properties);
}
}
if (scene->nodetree) {
bNode *node;
IDP_RelinkProperty(scene->nodetree->id.properties);
for (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 bool pyrna_id_CheckPyObject(PyObject *obj);
extern PyObject *pyrna_id_CreatePyObject(ID *id);
/*********************** 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;
}
}
@@ -478,6 +487,9 @@ bool BPy_IDProperty_Map_ValidateAndCreate(PyObject *name_obj, IDProperty *group,
Py_DECREF(ob_seq_fast);
}
else if (ob == Py_None || pyrna_id_FromPyObject(ob, &val.id)) {
prop = IDP_New(IDP_ID, &val, name);
}
else if (PyMapping_Check(ob)) {
PyObject *keys, *vals, *key, *pval;
int i, len;
@@ -613,6 +625,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 = (PyObject **) 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 = (PyObject **)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,12 @@ add_test(script_pyapi_idprop ${TEST_BLENDER_EXE}
--python ${CMAKE_CURRENT_LIST_DIR}/bl_pyapi_idprop.py
)
# ------------------------------------------------------------------------------
# PY DATABLOCK IDPROPERTIES TESTS
add_test(script_pyapi_datablock_idprop ${TEST_BLENDER_EXE}
--python ${CMAKE_CURRENT_LIST_DIR}/bl_datablock_idprop.py
)
# ------------------------------------------------------------------------------
# MODELING TESTS
add_test(bevel ${TEST_BLENDER_EXE}

View File

@@ -0,0 +1,334 @@
# ##### 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_homefile()
# 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_homefile()
# 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 test0():
bpy.ops.wm.read_homefile()
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 test1():
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 test2():
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 test3():
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 test4():
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()
test0()
test1()
test2()
check_crash(test3)
test4()
if __name__ == "__main__":
try:
main()
except:
import traceback
traceback.print_exc()
sys.stderr.flush()
os._exit(1)