This repository has been archived on 2023-10-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files
blender-archive/source/blender/makesrna/intern/rna_access_compare_override.c
luzpaz f159d49f56 Cleanup: Fix various source typos
This is a continuation of D13462 to clean up source typos.

Differential Revision: https://developer.blender.org/D13471
2021-12-06 22:39:52 -05:00

1473 lines
57 KiB
C

/*
* 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.
*/
/** \file
* \ingroup RNA
*/
#include <string.h>
#include <CLG_log.h>
#include "MEM_guardedalloc.h"
#include "DNA_ID.h"
#include "DNA_anim_types.h"
#include "DNA_constraint_types.h"
#include "DNA_gpencil_modifier_types.h"
#include "DNA_key_types.h"
#include "DNA_modifier_types.h"
#include "DNA_object_types.h"
#include "BLI_listbase.h"
#include "BLI_string.h"
#include "BLI_utildefines.h"
//#define DEBUG_OVERRIDE_TIMEIT
#ifdef DEBUG_OVERRIDE_TIMEIT
# include "PIL_time_utildefines.h"
#endif
#include "BKE_armature.h"
#include "BKE_idprop.h"
#include "BKE_idtype.h"
#include "BKE_lib_override.h"
#include "BKE_main.h"
#include "RNA_access.h"
#include "RNA_define.h"
#include "RNA_enum_types.h"
#include "rna_access_internal.h"
#include "rna_internal.h"
static CLG_LogRef LOG = {"rna.access_compare_override"};
/**
* Find the actual ID owner of the given \a ptr #PointerRNA, in override sense, and generate the
* full rna path from it to given \a prop #PropertyRNA if \a rna_path is given.
*
* \note This is slightly different than 'generic' RNA 'id owner' as returned by
* #RNA_find_real_ID_and_path, since in overrides we also consider shape keys as embedded data, not
* only root node trees and master collections.
*/
static ID *rna_property_override_property_real_id_owner(Main *bmain,
PointerRNA *ptr,
PropertyRNA *prop,
char **r_rna_path)
{
ID *id = ptr->owner_id;
ID *owner_id = id;
const char *rna_path_prefix = NULL;
if (r_rna_path != NULL) {
*r_rna_path = NULL;
}
if (id == NULL) {
return NULL;
}
if (id->flag & (LIB_EMBEDDED_DATA | LIB_EMBEDDED_DATA_LIB_OVERRIDE)) {
/* XXX this is very bad band-aid code, but for now it will do.
* We should at least use a #define for those prop names.
* Ideally RNA as a whole should be aware of those PITA of embedded IDs, and have a way to
* retrieve their owner IDs and generate paths from those.
*/
switch (GS(id->name)) {
case ID_KE:
owner_id = ((Key *)id)->from;
rna_path_prefix = "shape_keys.";
break;
case ID_GR:
case ID_NT:
/* Master collections, Root node trees. */
owner_id = RNA_find_real_ID_and_path(bmain, id, &rna_path_prefix);
break;
default:
BLI_assert(0);
}
}
if (r_rna_path == NULL) {
return owner_id;
}
char *rna_path = RNA_path_from_ID_to_property(ptr, prop);
if (rna_path) {
*r_rna_path = rna_path;
if (rna_path_prefix != NULL) {
*r_rna_path = BLI_sprintfN("%s%s", rna_path_prefix, rna_path);
MEM_freeN(rna_path);
}
return owner_id;
}
return NULL;
}
int RNA_property_override_flag(PropertyRNA *prop)
{
return rna_ensure_property(prop)->flag_override;
}
/** \note Does not take into account editable status, this has to be checked separately
* (using #RNA_property_editable_flag() usually). */
bool RNA_property_overridable_get(PointerRNA *ptr, PropertyRNA *prop)
{
if (prop->magic == RNA_MAGIC) {
/* Special handling for insertions of constraints or modifiers... */
/* TODO: Note We may want to add a more generic system to RNA
* (like a special property in struct of items)
* if we get more overridable collections,
* for now we can live with those special-cases handling I think. */
if (RNA_struct_is_a(ptr->type, &RNA_Constraint)) {
bConstraint *con = ptr->data;
if (con->flag & CONSTRAINT_OVERRIDE_LIBRARY_LOCAL) {
return true;
}
}
else if (RNA_struct_is_a(ptr->type, &RNA_Modifier)) {
ModifierData *mod = ptr->data;
if (mod->flag & eModifierFlag_OverrideLibrary_Local) {
return true;
}
}
else if (RNA_struct_is_a(ptr->type, &RNA_GpencilModifier)) {
GpencilModifierData *gp_mod = ptr->data;
if (gp_mod->flag & eGpencilModifierFlag_OverrideLibrary_Local) {
return true;
}
}
else if (RNA_struct_is_a(ptr->type, &RNA_NlaTrack)) {
NlaTrack *nla_track = ptr->data;
if (nla_track->flag & NLATRACK_OVERRIDELIBRARY_LOCAL) {
return true;
}
}
/* If this is a RNA-defined property (real or 'virtual' IDProp),
* we want to use RNA prop flag. */
return !(prop->flag_override & PROPOVERRIDE_NO_COMPARISON) &&
(prop->flag_override & PROPOVERRIDE_OVERRIDABLE_LIBRARY);
}
/* If this is a real 'pure' IDProp (aka custom property), we want to use the IDProp flag. */
IDProperty *idprop = (IDProperty *)prop;
return (idprop->flag & IDP_FLAG_OVERRIDABLE_LIBRARY) != 0;
}
/* Should only be used for custom properties */
bool RNA_property_overridable_library_set(PointerRNA *UNUSED(ptr),
PropertyRNA *prop,
const bool is_overridable)
{
/* Only works for pure custom properties IDProps. */
if (prop->magic != RNA_MAGIC) {
IDProperty *idprop = (IDProperty *)prop;
idprop->flag = is_overridable ? (idprop->flag | IDP_FLAG_OVERRIDABLE_LIBRARY) :
(idprop->flag & ~IDP_FLAG_OVERRIDABLE_LIBRARY);
return true;
}
return false;
}
bool RNA_property_overridden(PointerRNA *ptr, PropertyRNA *prop)
{
char *rna_path = RNA_path_from_ID_to_property(ptr, prop);
ID *id = ptr->owner_id;
if (rna_path == NULL || id == NULL || !ID_IS_OVERRIDE_LIBRARY(id)) {
return false;
}
return (BKE_lib_override_library_property_find(id->override_library, rna_path) != NULL);
}
bool RNA_property_comparable(PointerRNA *UNUSED(ptr), PropertyRNA *prop)
{
prop = rna_ensure_property(prop);
return !(prop->flag_override & PROPOVERRIDE_NO_COMPARISON);
}
static bool rna_property_override_operation_apply(Main *bmain,
PointerRNA *ptr_dst,
PointerRNA *ptr_src,
PointerRNA *ptr_storage,
PropertyRNA *prop_dst,
PropertyRNA *prop_src,
PropertyRNA *prop_storage,
PointerRNA *ptr_item_dst,
PointerRNA *ptr_item_src,
PointerRNA *ptr_item_storage,
IDOverrideLibraryPropertyOperation *opop);
bool RNA_property_copy(
Main *bmain, PointerRNA *ptr, PointerRNA *fromptr, PropertyRNA *prop, int index)
{
if (!RNA_property_editable(ptr, prop)) {
return false;
}
PropertyRNA *prop_dst = prop;
PropertyRNA *prop_src = prop;
/* Ensure we get real property data,
* be it an actual RNA property, or an #IDProperty in disguise. */
prop_dst = rna_ensure_property_realdata(&prop_dst, ptr);
prop_src = rna_ensure_property_realdata(&prop_src, fromptr);
/* IDprops: destination may not exist, if source does and is set, try to create it. */
/* NOTE: this is sort of quick hack/bandage to fix the issue,
* we need to rethink how IDProps are handled in 'diff' RNA code completely, imho... */
if (prop_src != NULL && prop_dst == NULL && RNA_property_is_set(fromptr, prop)) {
BLI_assert(prop_src->magic != RNA_MAGIC);
IDProperty *idp_dst = RNA_struct_idprops(ptr, true);
IDProperty *prop_idp_dst = IDP_CopyProperty((IDProperty *)prop_src);
IDP_AddToGroup(idp_dst, prop_idp_dst);
rna_idproperty_touch(prop_idp_dst);
/* Nothing else to do here... */
return true;
}
if (ELEM(NULL, prop_dst, prop_src)) {
return false;
}
IDOverrideLibraryPropertyOperation opop = {
.operation = IDOVERRIDE_LIBRARY_OP_REPLACE,
.subitem_reference_index = index,
.subitem_local_index = index,
};
return rna_property_override_operation_apply(
bmain, ptr, fromptr, NULL, prop_dst, prop_src, NULL, NULL, NULL, NULL, &opop);
}
static int rna_property_override_diff(Main *bmain,
PropertyRNAOrID *prop_a,
PropertyRNAOrID *prop_b,
const char *rna_path,
const size_t rna_path_len,
eRNACompareMode mode,
IDOverrideLibrary *override,
const eRNAOverrideMatch flags,
eRNAOverrideMatchResult *r_report_flags);
bool RNA_property_equals(
Main *bmain, PointerRNA *ptr_a, PointerRNA *ptr_b, PropertyRNA *prop, eRNACompareMode mode)
{
BLI_assert(ELEM(mode, RNA_EQ_STRICT, RNA_EQ_UNSET_MATCH_ANY, RNA_EQ_UNSET_MATCH_NONE));
PropertyRNAOrID prop_a, prop_b;
rna_property_rna_or_id_get(prop, ptr_a, &prop_a);
rna_property_rna_or_id_get(prop, ptr_b, &prop_b);
return (rna_property_override_diff(bmain, &prop_a, &prop_b, NULL, 0, mode, NULL, 0, NULL) == 0);
}
bool RNA_struct_equals(Main *bmain, PointerRNA *ptr_a, PointerRNA *ptr_b, eRNACompareMode mode)
{
CollectionPropertyIterator iter;
PropertyRNA *iterprop;
bool equals = true;
if (ptr_a == NULL && ptr_b == NULL) {
return true;
}
if (ptr_a == NULL || ptr_b == NULL) {
return false;
}
if (ptr_a->type != ptr_b->type) {
return false;
}
iterprop = RNA_struct_iterator_property(ptr_a->type);
RNA_property_collection_begin(ptr_a, iterprop, &iter);
for (; iter.valid; RNA_property_collection_next(&iter)) {
PropertyRNA *prop = iter.ptr.data;
if (!RNA_property_equals(bmain, ptr_a, ptr_b, prop, mode)) {
equals = false;
break;
}
}
RNA_property_collection_end(&iter);
return equals;
}
/* Low-level functions, also used by non-override RNA API like copy or equality check. */
/**
* Generic RNA property diff function.
*
* \note about \a prop and \a prop_a/prop_b parameters:
* the former is expected to be an 'un-resolved' one,
* while the two later are expected to be fully resolved ones
* (i.e. to be the IDProps when they should be, etc.).
* When \a prop is given, \a prop_a and \a prop_b should always be NULL, and vice-versa.
* This is necessary, because we cannot perform 'set/unset' checks on resolved properties
* (unset IDProps would merely be NULL then).
*
* \note When there is no equality,
* but we cannot determine an order (greater than/lesser than), we return 1.
*/
static int rna_property_override_diff(Main *bmain,
PropertyRNAOrID *prop_a,
PropertyRNAOrID *prop_b,
const char *rna_path,
const size_t rna_path_len,
eRNACompareMode mode,
IDOverrideLibrary *override,
const eRNAOverrideMatch flags,
eRNAOverrideMatchResult *r_report_flags)
{
BLI_assert(!ELEM(NULL, prop_a, prop_b));
if (prop_a->rnaprop->flag_override & PROPOVERRIDE_NO_COMPARISON ||
prop_b->rnaprop->flag_override & PROPOVERRIDE_NO_COMPARISON) {
return 0;
}
if (mode == RNA_EQ_UNSET_MATCH_ANY) {
/* Unset properties are assumed to match anything. */
if (!prop_a->is_set || !prop_b->is_set) {
return 0;
}
}
else if (mode == RNA_EQ_UNSET_MATCH_NONE) {
/* Unset properties never match set properties. */
if (prop_a->is_set != prop_b->is_set) {
return 1;
}
}
if (prop_a->is_idprop && ELEM(NULL, prop_a->idprop, prop_b->idprop)) {
return (prop_a->idprop == prop_b->idprop) ? 0 : 1;
}
/* Check if we are working with arrays. */
const bool is_array_a = prop_a->is_array;
const bool is_array_b = prop_b->is_array;
if (is_array_a != is_array_b) {
/* Should probably never happen actually... */
BLI_assert(0);
return is_array_a ? 1 : -1;
}
/* Get the length of the array to work with. */
const uint len_a = prop_a->array_len;
const uint len_b = prop_b->array_len;
if (len_a != len_b) {
/* Do not handle override in that case,
* we do not support insertion/deletion from arrays for now. */
return len_a > len_b ? 1 : -1;
}
if (is_array_a && len_a == 0) {
/* Empty arrays, will happen in some case with dynamic ones. */
return 0;
}
RNAPropOverrideDiff override_diff = NULL;
/* Special case for IDProps, we use default callback then. */
if (prop_a->is_idprop) {
override_diff = rna_property_override_diff_default;
if (!prop_b->is_idprop && prop_b->rnaprop->override_diff != override_diff) {
override_diff = NULL;
}
}
else if (prop_b->is_idprop) {
override_diff = rna_property_override_diff_default;
if (prop_a->rnaprop->override_diff != override_diff) {
override_diff = NULL;
}
}
else if (prop_a->rnaprop->override_diff == prop_b->rnaprop->override_diff) {
override_diff = prop_a->rnaprop->override_diff;
if (override_diff == NULL) {
override_diff = rna_property_override_diff_default;
}
}
if (override_diff == NULL) {
CLOG_ERROR(&LOG,
"'%s' gives unmatching or NULL RNA diff callbacks, should not happen (%d vs. %d)",
rna_path ? rna_path : prop_a->identifier,
!prop_a->is_idprop,
!prop_b->is_idprop);
BLI_assert(0);
return 1;
}
bool override_changed = false;
eRNAOverrideMatch diff_flags = flags;
if (!RNA_property_overridable_get(&prop_a->ptr, prop_a->rawprop) ||
(!ELEM(RNA_property_type(prop_a->rawprop), PROP_POINTER, PROP_COLLECTION) &&
!RNA_property_editable_flag(&prop_a->ptr, prop_a->rawprop))) {
diff_flags &= ~RNA_OVERRIDE_COMPARE_CREATE;
}
const int diff = override_diff(bmain,
prop_a,
prop_b,
mode,
override,
rna_path,
rna_path_len,
diff_flags,
&override_changed);
if (override_changed && r_report_flags) {
*r_report_flags |= RNA_OVERRIDE_MATCH_RESULT_CREATED;
}
return diff;
}
/* Modify local data-block to make it ready for override application
* (only needed for diff operations, where we use
* the local data-block's data as second operand). */
static bool rna_property_override_operation_store(Main *bmain,
PointerRNA *ptr_local,
PointerRNA *ptr_reference,
PointerRNA *ptr_storage,
PropertyRNA *prop_local,
PropertyRNA *prop_reference,
PropertyRNA *prop_storage,
IDOverrideLibraryProperty *op)
{
int len_local, len_reference, len_storage = 0;
bool changed = false;
if (ptr_storage == NULL) {
return changed;
}
/* get the length of the array to work with */
len_local = RNA_property_array_length(ptr_local, prop_local);
len_reference = RNA_property_array_length(ptr_reference, prop_reference);
if (prop_storage) {
len_storage = RNA_property_array_length(ptr_storage, prop_storage);
}
if (len_local != len_reference || len_local != len_storage) {
/* Do not handle override in that case,
* we do not support insertion/deletion from arrays for now. */
return changed;
}
RNAPropOverrideStore override_store = NULL;
/* Special case for IDProps, we use default callback then. */
if (prop_local->magic != RNA_MAGIC) {
override_store = rna_property_override_store_default;
if (prop_reference->magic == RNA_MAGIC && prop_reference->override_store != override_store) {
override_store = NULL;
}
}
else if (prop_reference->magic != RNA_MAGIC) {
override_store = rna_property_override_store_default;
if (prop_local->override_store != override_store) {
override_store = NULL;
}
}
else if (prop_local->override_store == prop_reference->override_store) {
override_store = prop_local->override_store;
if (override_store == NULL) {
override_store = rna_property_override_store_default;
}
}
if (ptr_storage != NULL && prop_storage->magic == RNA_MAGIC &&
!ELEM(prop_storage->override_store, NULL, override_store)) {
override_store = NULL;
}
if (override_store == NULL) {
CLOG_ERROR(&LOG,
"'%s' gives unmatching or NULL RNA store callbacks, should not happen (%d vs. %d)",
op->rna_path,
prop_local->magic == RNA_MAGIC,
prop_reference->magic == RNA_MAGIC);
BLI_assert(0);
return changed;
}
LISTBASE_FOREACH (IDOverrideLibraryPropertyOperation *, opop, &op->operations) {
/* Only needed for diff operations. */
if (!ELEM(opop->operation,
IDOVERRIDE_LIBRARY_OP_ADD,
IDOVERRIDE_LIBRARY_OP_SUBTRACT,
IDOVERRIDE_LIBRARY_OP_MULTIPLY)) {
continue;
}
if (override_store(bmain,
ptr_local,
ptr_reference,
ptr_storage,
prop_local,
prop_reference,
prop_storage,
len_local,
len_reference,
len_storage,
opop)) {
changed = true;
}
}
return changed;
}
static bool rna_property_override_operation_apply(Main *bmain,
PointerRNA *ptr_dst,
PointerRNA *ptr_src,
PointerRNA *ptr_storage,
PropertyRNA *prop_dst,
PropertyRNA *prop_src,
PropertyRNA *prop_storage,
PointerRNA *ptr_item_dst,
PointerRNA *ptr_item_src,
PointerRNA *ptr_item_storage,
IDOverrideLibraryPropertyOperation *opop)
{
int len_dst, len_src, len_storage = 0;
const short override_op = opop->operation;
if (!BKE_lib_override_library_property_operation_operands_validate(
opop, ptr_dst, ptr_src, ptr_storage, prop_dst, prop_src, prop_storage)) {
return false;
}
if (override_op == IDOVERRIDE_LIBRARY_OP_NOOP) {
return true;
}
RNAPropOverrideApply override_apply = NULL;
/* Special case for IDProps, we use default callback then. */
if (prop_dst->magic != RNA_MAGIC) {
override_apply = rna_property_override_apply_default;
if (prop_src->magic == RNA_MAGIC && !ELEM(prop_src->override_apply, NULL, override_apply)) {
override_apply = NULL;
}
}
else if (prop_src->magic != RNA_MAGIC) {
override_apply = rna_property_override_apply_default;
if (!ELEM(prop_dst->override_apply, NULL, override_apply)) {
override_apply = NULL;
}
}
else if (prop_dst->override_apply == prop_src->override_apply) {
override_apply = prop_dst->override_apply;
if (override_apply == NULL) {
override_apply = rna_property_override_apply_default;
}
}
if (ptr_storage && prop_storage->magic == RNA_MAGIC &&
!ELEM(prop_storage->override_apply, NULL, override_apply)) {
override_apply = NULL;
}
if (override_apply == NULL) {
CLOG_ERROR(&LOG,
"'%s' gives unmatching or NULL RNA apply callbacks, should not happen (%d vs. %d)",
prop_dst->magic != RNA_MAGIC ? ((IDProperty *)prop_dst)->name :
prop_dst->identifier,
prop_dst->magic == RNA_MAGIC,
prop_src->magic == RNA_MAGIC);
BLI_assert(0);
return false;
}
/* get the length of the array to work with */
len_dst = RNA_property_array_length(ptr_dst, prop_dst);
len_src = RNA_property_array_length(ptr_src, prop_src);
if (ptr_storage) {
len_storage = RNA_property_array_length(ptr_storage, prop_storage);
}
if (len_dst != len_src || (ptr_storage && len_dst != len_storage)) {
/* Do not handle override in that case,
* we do not support insertion/deletion from arrays for now. */
return false;
}
/* get and set the default values as appropriate for the various types */
const bool success = override_apply(bmain,
ptr_dst,
ptr_src,
ptr_storage,
prop_dst,
prop_src,
prop_storage,
len_dst,
len_src,
len_storage,
ptr_item_dst,
ptr_item_src,
ptr_item_storage,
opop);
if (success) {
RNA_property_update_main(bmain, NULL, ptr_dst, prop_dst);
}
return success;
}
/**
* Check whether reference and local overridden data match (are the same),
* with respect to given restrictive sets of properties.
* If requested, will generate needed new property overrides, and/or restore values from reference.
*
* \param r_report_flags: If given,
* will be set with flags matching actions taken by the function on \a ptr_local.
*
* \return True if _resulting_ \a ptr_local does match \a ptr_reference.
*/
bool RNA_struct_override_matches(Main *bmain,
PointerRNA *ptr_local,
PointerRNA *ptr_reference,
const char *root_path,
const size_t root_path_len,
IDOverrideLibrary *override,
const eRNAOverrideMatch flags,
eRNAOverrideMatchResult *r_report_flags)
{
CollectionPropertyIterator iter;
PropertyRNA *iterprop;
bool matching = true;
BLI_assert(ptr_local->type == ptr_reference->type);
BLI_assert(ptr_local->owner_id && ptr_reference->owner_id);
const bool ignore_non_overridable = (flags & RNA_OVERRIDE_COMPARE_IGNORE_NON_OVERRIDABLE) != 0;
const bool ignore_overridden = (flags & RNA_OVERRIDE_COMPARE_IGNORE_OVERRIDDEN) != 0;
const bool do_create = (flags & RNA_OVERRIDE_COMPARE_CREATE) != 0;
const bool do_restore = (flags & RNA_OVERRIDE_COMPARE_RESTORE) != 0;
#ifdef DEBUG_OVERRIDE_TIMEIT
static float _sum_time_global = 0.0f;
static float _num_time_global = 0.0f;
double _timeit_time_global;
static float _sum_time_diffing = 0.0f;
static float _delta_time_diffing = 0.0f;
static int _num_delta_time_diffing = 0.0f;
static float _num_time_diffing = 0.0f;
double _timeit_time_diffing;
if (!root_path) {
_delta_time_diffing = 0.0f;
_num_delta_time_diffing = 0;
_timeit_time_global = PIL_check_seconds_timer();
}
#endif
if (ptr_local->owner_id == ptr_local->data && GS(ptr_local->owner_id->name) == ID_OB) {
/* Our beloved pose's bone cross-data pointers. Usually, depsgraph evaluation would
* ensure this is valid, but in some situations (like hidden collections etc.) this won't
* be the case, so we need to take care of this ourselves.
*
* NOTE: Typically callers of this function (from BKE_lib_override area) will already have
* ensured this. However, studio is still reporting sporadic, unreproducible crashes due to
* invalid pose data, so think there are still some cases where some armatures are somehow
* missing updates (possibly due to dependencies?). Since calling this function on same ID
* several time is almost free, and safe even in a threaded context as long as it has been done
* at least once first outside of threaded processing, we do it another time here. */
Object *ob_local = (Object *)ptr_local->owner_id;
if (ob_local->type == OB_ARMATURE) {
Object *ob_reference = (Object *)ptr_local->owner_id->override_library->reference;
BLI_assert(ob_local->data != NULL);
BLI_assert(ob_reference->data != NULL);
BKE_pose_ensure(bmain, ob_local, ob_local->data, true);
BKE_pose_ensure(bmain, ob_reference, ob_reference->data, true);
}
}
iterprop = RNA_struct_iterator_property(ptr_local->type);
for (RNA_property_collection_begin(ptr_local, iterprop, &iter); iter.valid;
RNA_property_collection_next(&iter)) {
PropertyRNA *rawprop = iter.ptr.data;
PropertyRNAOrID prop_local;
PropertyRNAOrID prop_reference;
rna_property_rna_or_id_get(rawprop, ptr_local, &prop_local);
rna_property_rna_or_id_get(rawprop, ptr_reference, &prop_reference);
BLI_assert(prop_local.rnaprop != NULL);
BLI_assert(prop_local.rnaprop == prop_reference.rnaprop);
BLI_assert(prop_local.is_idprop == prop_reference.is_idprop);
if ((prop_local.is_idprop && prop_local.idprop == NULL) ||
(prop_reference.is_idprop && prop_reference.idprop == NULL)) {
continue;
}
if (ignore_non_overridable && !RNA_property_overridable_get(&prop_local.ptr, rawprop)) {
continue;
}
if (!prop_local.is_idprop &&
RNA_property_override_flag(prop_local.rnaprop) & PROPOVERRIDE_IGNORE) {
continue;
}
#if 0 /* This actually makes things slower, since it has to check for animation paths etc! */
if (RNA_property_animated(ptr_local, prop_local)) {
/* We cannot do anything here really, animation is some kind of dynamic overrides that has
* precedence over static one... */
continue;
}
#endif
#define RNA_PATH_BUFFSIZE 8192
char rna_path_buffer[RNA_PATH_BUFFSIZE];
char *rna_path = rna_path_buffer;
size_t rna_path_len = 0;
/* XXX TODO: this will have to be refined to handle collections insertions, and array items. */
if (root_path) {
BLI_assert(strlen(root_path) == root_path_len);
const char *prop_name = prop_local.identifier;
const size_t prop_name_len = strlen(prop_name);
/* Inlined building (significantly more efficient). */
if (!prop_local.is_idprop) {
rna_path_len = root_path_len + 1 + prop_name_len;
if (rna_path_len >= RNA_PATH_BUFFSIZE) {
rna_path = MEM_mallocN(rna_path_len + 1, __func__);
}
memcpy(rna_path, root_path, root_path_len);
rna_path[root_path_len] = '.';
memcpy(rna_path + root_path_len + 1, prop_name, prop_name_len);
rna_path[rna_path_len] = '\0';
}
else {
rna_path_len = root_path_len + 2 + prop_name_len + 2;
if (rna_path_len >= RNA_PATH_BUFFSIZE) {
rna_path = MEM_mallocN(rna_path_len + 1, __func__);
}
memcpy(rna_path, root_path, root_path_len);
rna_path[root_path_len] = '[';
rna_path[root_path_len + 1] = '"';
memcpy(rna_path + root_path_len + 2, prop_name, prop_name_len);
rna_path[root_path_len + 2 + prop_name_len] = '"';
rna_path[root_path_len + 2 + prop_name_len + 1] = ']';
rna_path[rna_path_len] = '\0';
}
}
else {
/* This is rather slow, but is not much called, so not really worth optimizing. */
rna_path = RNA_path_from_ID_to_property(ptr_local, rawprop);
if (rna_path != NULL) {
rna_path_len = strlen(rna_path);
}
}
if (rna_path == NULL) {
continue;
}
CLOG_INFO(&LOG, 5, "Override Checking %s", rna_path);
IDOverrideLibraryProperty *op = BKE_lib_override_library_property_find(override, rna_path);
if (ignore_overridden && op != NULL) {
BKE_lib_override_library_operations_tag(op, IDOVERRIDE_LIBRARY_TAG_UNUSED, false);
if (rna_path != rna_path_buffer) {
MEM_freeN(rna_path);
}
continue;
}
#ifdef DEBUG_OVERRIDE_TIMEIT
if (!root_path) {
_timeit_time_diffing = PIL_check_seconds_timer();
}
#endif
eRNAOverrideMatchResult report_flags = 0;
const int diff = rna_property_override_diff(bmain,
&prop_local,
&prop_reference,
rna_path,
rna_path_len,
RNA_EQ_STRICT,
override,
flags,
&report_flags);
#ifdef DEBUG_OVERRIDE_TIMEIT
if (!root_path) {
const float _delta_time = (float)(PIL_check_seconds_timer() - _timeit_time_diffing);
_delta_time_diffing += _delta_time;
_num_delta_time_diffing++;
}
#endif
matching = matching && diff == 0;
if (r_report_flags) {
*r_report_flags |= report_flags;
}
if (diff != 0) {
/* XXX TODO: refine this for per-item overriding of arrays... */
op = BKE_lib_override_library_property_find(override, rna_path);
IDOverrideLibraryPropertyOperation *opop = op ? op->operations.first : NULL;
if (op != NULL) {
BKE_lib_override_library_operations_tag(op, IDOVERRIDE_LIBRARY_TAG_UNUSED, false);
}
if (do_restore && (report_flags & RNA_OVERRIDE_MATCH_RESULT_CREATED) == 0) {
/* We are allowed to restore to reference's values. */
if (ELEM(NULL, op, opop) || opop->operation == IDOVERRIDE_LIBRARY_OP_NOOP) {
/* We should restore that property to its reference value */
if (RNA_property_editable(ptr_local, rawprop)) {
IDOverrideLibraryPropertyOperation opop_tmp = {
.operation = IDOVERRIDE_LIBRARY_OP_REPLACE,
.subitem_reference_index = -1,
.subitem_local_index = -1,
};
rna_property_override_operation_apply(bmain,
ptr_local,
ptr_reference,
NULL,
rawprop,
rawprop,
NULL,
NULL,
NULL,
NULL,
&opop_tmp);
if (r_report_flags) {
*r_report_flags |= RNA_OVERRIDE_MATCH_RESULT_RESTORED;
}
}
else {
/* Too noisy for now, this triggers on runtime props like transform matrices etc. */
#if 0
BLI_assert_msg(0, "We have differences between reference and "
"overriding data on non-editable property.");
#endif
matching = false;
}
}
}
else if ((report_flags & RNA_OVERRIDE_MATCH_RESULT_CREATED) == 0 && ELEM(NULL, op, opop)) {
/* This property is not overridden, and differs from reference, so we have no match. */
matching = false;
if (!(do_create || do_restore)) {
/* Since we have no 'changing' action allowed, we can break here. */
if (rna_path != rna_path_buffer) {
MEM_freeN(rna_path);
}
break;
}
}
}
if (rna_path != rna_path_buffer) {
MEM_freeN(rna_path);
}
#undef RNA_PATH_BUFFSIZE
}
RNA_property_collection_end(&iter);
#ifdef DEBUG_OVERRIDE_TIMEIT
if (!root_path) {
const float _delta_time = (float)(PIL_check_seconds_timer() - _timeit_time_global);
_sum_time_global += _delta_time;
_num_time_global++;
_sum_time_diffing += _delta_time_diffing;
_num_time_diffing++;
printf("ID: %s\n", ((ID *)ptr_local->owner_id)->name);
printf("time end (%s): %.6f\n", __func__, _delta_time);
printf("time averaged (%s): %.6f (total: %.6f, in %d runs)\n",
__func__,
(_sum_time_global / _num_time_global),
_sum_time_global,
(int)_num_time_global);
printf("diffing time end (%s): %.6f (in %d runs)\n",
__func__,
_delta_time_diffing,
_num_delta_time_diffing);
printf("diffing time averaged (%s): %.6f (total: %.6f, in %d runs)\n",
__func__,
(_sum_time_diffing / _num_time_diffing),
_sum_time_diffing,
(int)_num_time_diffing);
}
#endif
return matching;
}
/**
* Store needed second operands into \a storage data-block
* for differential override operations.
*/
bool RNA_struct_override_store(Main *bmain,
PointerRNA *ptr_local,
PointerRNA *ptr_reference,
PointerRNA *ptr_storage,
IDOverrideLibrary *override)
{
bool changed = false;
#ifdef DEBUG_OVERRIDE_TIMEIT
TIMEIT_START_AVERAGED(RNA_struct_override_store);
#endif
LISTBASE_FOREACH (IDOverrideLibraryProperty *, op, &override->properties) {
/* Simplified for now! */
PointerRNA data_reference, data_local;
PropertyRNA *prop_reference, *prop_local;
if (RNA_path_resolve_property(ptr_local, op->rna_path, &data_local, &prop_local) &&
RNA_path_resolve_property(ptr_reference, op->rna_path, &data_reference, &prop_reference)) {
PointerRNA data_storage;
PropertyRNA *prop_storage = NULL;
/* It is totally OK if this does not success,
* only a subset of override operations actually need storage. */
if (ptr_storage && (ptr_storage->owner_id != NULL)) {
RNA_path_resolve_property(ptr_storage, op->rna_path, &data_storage, &prop_storage);
}
if (rna_property_override_operation_store(bmain,
&data_local,
&data_reference,
&data_storage,
prop_reference,
prop_local,
prop_storage,
op)) {
changed = true;
}
}
}
#ifdef DEBUG_OVERRIDE_TIMEIT
TIMEIT_END_AVERAGED(RNA_struct_override_store);
#endif
return changed;
}
static void rna_porperty_override_collection_subitem_lookup(
PointerRNA *ptr_dst,
PointerRNA *ptr_src,
PointerRNA *ptr_storage,
PropertyRNA *prop_dst,
PropertyRNA *prop_src,
PropertyRNA *prop_storage,
PointerRNA **r_ptr_item_dst,
PointerRNA **r_ptr_item_src,
PointerRNA **r_ptr_item_storage,
PointerRNA *private_ptr_item_dst,
PointerRNA *private_ptr_item_src,
PointerRNA *private_ptr_item_storage,
IDOverrideLibraryProperty *op,
IDOverrideLibraryPropertyOperation *opop)
{
if ((RNA_property_type(prop_dst) != PROP_COLLECTION ||
RNA_property_type(prop_src) != PROP_COLLECTION ||
(prop_storage != NULL && RNA_property_type(prop_storage) != PROP_COLLECTION)) ||
(opop->subitem_local_name == NULL && opop->subitem_reference_name == NULL &&
opop->subitem_local_index == -1 && opop->subitem_reference_index == -1)) {
return;
}
RNA_POINTER_INVALIDATE(private_ptr_item_dst);
RNA_POINTER_INVALIDATE(private_ptr_item_src);
if (prop_storage != NULL) {
RNA_POINTER_INVALIDATE(private_ptr_item_storage);
}
if (opop->subitem_local_name != NULL) {
RNA_property_collection_lookup_string(
ptr_src, prop_src, opop->subitem_local_name, private_ptr_item_src);
if (opop->subitem_reference_name != NULL &&
RNA_property_collection_lookup_string(
ptr_dst, prop_dst, opop->subitem_reference_name, private_ptr_item_dst)) {
/* This is rather fragile, but the fact that local override IDs may have a different name
* than their linked reference makes it necessary.
* Basically, here we are considering that if we cannot find the original linked ID in
* the local override we are (re-)applying the operations, then it may be because some of
* those operations have already been applied, and we may already have the local ID
* pointer we want to set.
* This happens e.g. during re-sync of an override, since we have already remapped all ID
* pointers to their expected values.
* In that case we simply try to get the property from the local expected name. */
}
else {
RNA_property_collection_lookup_string(
ptr_dst, prop_dst, opop->subitem_local_name, private_ptr_item_dst);
}
}
else if (opop->subitem_reference_name != NULL) {
RNA_property_collection_lookup_string(
ptr_src, prop_src, opop->subitem_reference_name, private_ptr_item_src);
RNA_property_collection_lookup_string(
ptr_dst, prop_dst, opop->subitem_reference_name, private_ptr_item_dst);
}
else if (opop->subitem_local_index != -1) {
RNA_property_collection_lookup_int(
ptr_src, prop_src, opop->subitem_local_index, private_ptr_item_src);
if (opop->subitem_reference_index != -1) {
RNA_property_collection_lookup_int(
ptr_dst, prop_dst, opop->subitem_reference_index, private_ptr_item_dst);
}
else {
RNA_property_collection_lookup_int(
ptr_dst, prop_dst, opop->subitem_local_index, private_ptr_item_dst);
}
}
else if (opop->subitem_reference_index != -1) {
RNA_property_collection_lookup_int(
ptr_src, prop_src, opop->subitem_reference_index, private_ptr_item_src);
RNA_property_collection_lookup_int(
ptr_dst, prop_dst, opop->subitem_reference_index, private_ptr_item_dst);
}
if (prop_storage != NULL) {
if (opop->subitem_local_name != NULL) {
RNA_property_collection_lookup_string(
ptr_storage, prop_storage, opop->subitem_local_name, private_ptr_item_storage);
}
else if (opop->subitem_reference_name != NULL) {
RNA_property_collection_lookup_string(
ptr_storage, prop_storage, opop->subitem_reference_name, private_ptr_item_storage);
}
else if (opop->subitem_local_index != -1) {
RNA_property_collection_lookup_int(
ptr_storage, prop_storage, opop->subitem_local_index, private_ptr_item_storage);
}
else if (opop->subitem_reference_index != -1) {
RNA_property_collection_lookup_int(
ptr_storage, prop_storage, opop->subitem_reference_index, private_ptr_item_storage);
}
}
*r_ptr_item_dst = private_ptr_item_dst;
*r_ptr_item_src = private_ptr_item_src;
if (prop_storage != NULL) {
*r_ptr_item_storage = private_ptr_item_storage;
}
if ((*r_ptr_item_dst)->type == NULL) {
CLOG_INFO(&LOG,
2,
"Failed to find destination sub-item '%s' (%d) of '%s' in new override data '%s'",
opop->subitem_reference_name,
opop->subitem_reference_index,
op->rna_path,
ptr_dst->owner_id->name);
}
if ((*r_ptr_item_src)->type == NULL) {
CLOG_INFO(&LOG,
2,
"Failed to find source sub-item '%s' (%d) of '%s' in old override data '%s'",
opop->subitem_local_name,
opop->subitem_local_index,
op->rna_path,
ptr_src->owner_id->name);
}
}
static void rna_property_override_check_resync(Main *bmain,
PointerRNA *ptr_dst,
PointerRNA *ptr_item_dst,
PointerRNA *ptr_item_src)
{
ID *id_owner = rna_property_override_property_real_id_owner(bmain, ptr_dst, NULL, NULL);
ID *id_src = rna_property_override_property_real_id_owner(bmain, ptr_item_src, NULL, NULL);
ID *id_dst = rna_property_override_property_real_id_owner(bmain, ptr_item_dst, NULL, NULL);
BLI_assert(id_src == NULL || ID_IS_OVERRIDE_LIBRARY_REAL(id_src));
/* Work around file corruption on writing, see T86853. */
if (id_src != NULL && !ID_IS_OVERRIDE_LIBRARY_REAL(id_src)) {
return;
}
if (/* We might be in a case where id_dst has already been processed and its usages
* remapped to its new local override. In that case overrides and linked data
* are always properly matching. */
id_src != id_dst &&
/* If one of the pointers is NULL and not the other, we are in a non-matching case. */
(ELEM(NULL, id_src, id_dst) ||
/* If `id_dst` is not from same lib as id_src, and linked reference ID of `id_src` is not
* `id_dst`, we are in a non-matching case. */
(id_dst->lib != id_src->lib && id_src->override_library->reference != id_dst) ||
/* If `id_dst` is from same lib as id_src, and is not same as `id_owner`, we are in a
* non-matching case.
*
* NOTE: Here we are testing if `id_owner` is referencing itself, in that case the new
* override copy generated by `BKE_lib_override_library_update` will already have its
* self-references updated to itself, instead of still pointing to its linked source. */
(id_dst->lib == id_src->lib && id_dst != id_owner))) {
ptr_dst->owner_id->tag |= LIB_TAG_LIB_OVERRIDE_NEED_RESYNC;
CLOG_INFO(&LOG, 3, "Local override %s detected as needing resync", ptr_dst->owner_id->name);
}
}
static void rna_property_override_apply_ex(Main *bmain,
PointerRNA *ptr_dst,
PointerRNA *ptr_src,
PointerRNA *ptr_storage,
PropertyRNA *prop_dst,
PropertyRNA *prop_src,
PropertyRNA *prop_storage,
PointerRNA *ptr_item_dst,
PointerRNA *ptr_item_src,
PointerRNA *ptr_item_storage,
IDOverrideLibraryProperty *op,
const bool do_insert)
{
LISTBASE_FOREACH (IDOverrideLibraryPropertyOperation *, opop, &op->operations) {
if (!do_insert != !ELEM(opop->operation,
IDOVERRIDE_LIBRARY_OP_INSERT_AFTER,
IDOVERRIDE_LIBRARY_OP_INSERT_BEFORE)) {
if (!do_insert) {
CLOG_INFO(&LOG, 5, "Skipping insert override operations in first pass (%s)", op->rna_path);
}
continue;
}
/* NOTE: will have to think about putting that logic into its own function maybe?
* Would be nice to have it in a single place...
* Note that here, src is the local saved ID, and dst is a copy of the linked ID (since we use
* local ID as storage to apply local changes on top of a clean copy of the linked data). */
PointerRNA private_ptr_item_dst, private_ptr_item_src, private_ptr_item_storage;
rna_porperty_override_collection_subitem_lookup(ptr_dst,
ptr_src,
ptr_storage,
prop_dst,
prop_src,
prop_storage,
&ptr_item_dst,
&ptr_item_src,
&ptr_item_storage,
&private_ptr_item_dst,
&private_ptr_item_src,
&private_ptr_item_storage,
op,
opop);
if (!rna_property_override_operation_apply(bmain,
ptr_dst,
ptr_src,
ptr_storage,
prop_dst,
prop_src,
prop_storage,
ptr_item_dst,
ptr_item_src,
ptr_item_storage,
opop)) {
CLOG_INFO(&LOG,
4,
"Failed to apply '%s' override operation on %s\n",
op->rna_path,
ptr_src->owner_id->name);
}
}
}
/**
* Apply given \a override operations on \a ptr_dst, using \a ptr_src
* (and \a ptr_storage for differential ops) as source.
*/
void RNA_struct_override_apply(Main *bmain,
PointerRNA *ptr_dst,
PointerRNA *ptr_src,
PointerRNA *ptr_storage,
IDOverrideLibrary *override,
const eRNAOverrideApplyFlag flag)
{
#ifdef DEBUG_OVERRIDE_TIMEIT
TIMEIT_START_AVERAGED(RNA_struct_override_apply);
#endif
/* NOTE: Applying insert operations in a separate pass is mandatory.
* We could optimize this later, but for now, as inefficient as it is,
* don't think this is a critical point.
*/
bool do_insert = false;
for (int i = 0; i < 2; i++, do_insert = true) {
LISTBASE_FOREACH (IDOverrideLibraryProperty *, op, &override->properties) {
/* Simplified for now! */
PointerRNA data_src, data_dst;
PointerRNA data_item_src, data_item_dst;
PropertyRNA *prop_src, *prop_dst;
if (RNA_path_resolve_property_and_item_pointer(
ptr_dst, op->rna_path, &data_dst, &prop_dst, &data_item_dst) &&
RNA_path_resolve_property_and_item_pointer(
ptr_src, op->rna_path, &data_src, &prop_src, &data_item_src)) {
PointerRNA data_storage, data_item_storage;
PropertyRNA *prop_storage = NULL;
/* It is totally OK if this does not success,
* only a subset of override operations actually need storage. */
if (ptr_storage && (ptr_storage->owner_id != NULL)) {
RNA_path_resolve_property_and_item_pointer(
ptr_storage, op->rna_path, &data_storage, &prop_storage, &data_item_storage);
}
/* Check if an overridden ID pointer supposed to be in sync with linked data gets out of
* sync. */
if ((ptr_dst->owner_id->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC) == 0) {
if (op->rna_prop_type == PROP_POINTER &&
(((IDOverrideLibraryPropertyOperation *)op->operations.first)->flag &
IDOVERRIDE_LIBRARY_FLAG_IDPOINTER_MATCH_REFERENCE) != 0) {
BLI_assert(RNA_struct_is_ID(RNA_property_pointer_type(&data_src, prop_src)));
BLI_assert(ptr_src->owner_id ==
rna_property_override_property_real_id_owner(bmain, &data_src, NULL, NULL));
BLI_assert(ptr_dst->owner_id ==
rna_property_override_property_real_id_owner(bmain, &data_dst, NULL, NULL));
PointerRNA prop_ptr_src = RNA_property_pointer_get(&data_src, prop_src);
PointerRNA prop_ptr_dst = RNA_property_pointer_get(&data_dst, prop_dst);
rna_property_override_check_resync(bmain, ptr_dst, &prop_ptr_dst, &prop_ptr_src);
}
else if (op->rna_prop_type == PROP_COLLECTION) {
if (RNA_struct_is_ID(RNA_property_pointer_type(&data_src, prop_src))) {
BLI_assert(ptr_src->owner_id == rna_property_override_property_real_id_owner(
bmain, &data_src, NULL, NULL));
BLI_assert(ptr_dst->owner_id == rna_property_override_property_real_id_owner(
bmain, &data_dst, NULL, NULL));
LISTBASE_FOREACH (IDOverrideLibraryPropertyOperation *, opop, &op->operations) {
if ((opop->flag & IDOVERRIDE_LIBRARY_FLAG_IDPOINTER_MATCH_REFERENCE) == 0) {
continue;
}
PointerRNA *ptr_item_dst, *ptr_item_src;
PointerRNA private_ptr_item_dst, private_ptr_item_src;
rna_porperty_override_collection_subitem_lookup(ptr_dst,
ptr_src,
NULL,
prop_dst,
prop_src,
NULL,
&ptr_item_dst,
&ptr_item_src,
NULL,
&private_ptr_item_dst,
&private_ptr_item_src,
NULL,
op,
opop);
rna_property_override_check_resync(bmain, ptr_dst, ptr_item_dst, ptr_item_src);
}
}
}
}
/* Workaround for older broken overrides, we then assume that non-matching ID pointers
* override operations that replace a non-NULL value are 'mistakes', and ignore (do not
* apply) them. */
if ((flag & RNA_OVERRIDE_APPLY_FLAG_IGNORE_ID_POINTERS) != 0 &&
op->rna_prop_type == PROP_POINTER &&
(((IDOverrideLibraryPropertyOperation *)op->operations.first)->flag &
IDOVERRIDE_LIBRARY_FLAG_IDPOINTER_MATCH_REFERENCE) == 0) {
BLI_assert(ptr_src->owner_id ==
rna_property_override_property_real_id_owner(bmain, &data_src, NULL, NULL));
BLI_assert(ptr_dst->owner_id ==
rna_property_override_property_real_id_owner(bmain, &data_dst, NULL, NULL));
PointerRNA prop_ptr_dst = RNA_property_pointer_get(&data_dst, prop_dst);
if (prop_ptr_dst.type != NULL && RNA_struct_is_ID(prop_ptr_dst.type)) {
#ifndef NDEBUG
PointerRNA prop_ptr_src = RNA_property_pointer_get(&data_src, prop_src);
BLI_assert(prop_ptr_src.type == NULL || RNA_struct_is_ID(prop_ptr_src.type));
#endif
ID *id_dst = rna_property_override_property_real_id_owner(
bmain, &prop_ptr_dst, NULL, NULL);
if (id_dst != NULL) {
CLOG_INFO(&LOG,
4,
"%s: Ignoring local override on ID pointer property '%s', as requested by "
"RNA_OVERRIDE_APPLY_FLAG_IGNORE_ID_POINTERS flag",
ptr_dst->owner_id->name,
op->rna_path);
continue;
}
}
}
rna_property_override_apply_ex(bmain,
&data_dst,
&data_src,
prop_storage ? &data_storage : NULL,
prop_dst,
prop_src,
prop_storage,
&data_item_dst,
&data_item_src,
prop_storage ? &data_item_storage : NULL,
op,
do_insert);
}
else {
CLOG_INFO(&LOG,
4,
"Failed to apply library override operation to '%s.%s' "
"(could not resolve some properties, local: %d, override: %d)",
((ID *)ptr_src->owner_id)->name,
op->rna_path,
RNA_path_resolve_property(ptr_dst, op->rna_path, &data_dst, &prop_dst),
RNA_path_resolve_property(ptr_src, op->rna_path, &data_src, &prop_src));
}
}
}
/* Some cases (like point caches) may require additional post-processing. */
if (RNA_struct_is_a(ptr_dst->type, &RNA_ID)) {
ID *id_dst = ptr_dst->data;
ID *id_src = ptr_src->data;
const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id_dst);
if (id_type->lib_override_apply_post != NULL) {
id_type->lib_override_apply_post(id_dst, id_src);
}
}
#ifdef DEBUG_OVERRIDE_TIMEIT
TIMEIT_END_AVERAGED(RNA_struct_override_apply);
#endif
}
IDOverrideLibraryProperty *RNA_property_override_property_find(Main *bmain,
PointerRNA *ptr,
PropertyRNA *prop,
ID **r_owner_id)
{
char *rna_path;
*r_owner_id = rna_property_override_property_real_id_owner(bmain, ptr, prop, &rna_path);
if (rna_path != NULL) {
IDOverrideLibraryProperty *op = BKE_lib_override_library_property_find(
(*r_owner_id)->override_library, rna_path);
MEM_freeN(rna_path);
return op;
}
return NULL;
}
IDOverrideLibraryProperty *RNA_property_override_property_get(Main *bmain,
PointerRNA *ptr,
PropertyRNA *prop,
bool *r_created)
{
char *rna_path;
if (r_created != NULL) {
*r_created = false;
}
ID *id = rna_property_override_property_real_id_owner(bmain, ptr, prop, &rna_path);
if (rna_path != NULL) {
IDOverrideLibraryProperty *op = BKE_lib_override_library_property_get(
id->override_library, rna_path, r_created);
MEM_freeN(rna_path);
return op;
}
return NULL;
}
IDOverrideLibraryPropertyOperation *RNA_property_override_property_operation_find(
Main *bmain,
PointerRNA *ptr,
PropertyRNA *prop,
const int index,
const bool strict,
bool *r_strict)
{
ID *owner_id;
IDOverrideLibraryProperty *op = RNA_property_override_property_find(bmain, ptr, prop, &owner_id);
if (!op) {
return NULL;
}
return BKE_lib_override_library_property_operation_find(
op, NULL, NULL, index, index, strict, r_strict);
}
IDOverrideLibraryPropertyOperation *RNA_property_override_property_operation_get(
Main *bmain,
PointerRNA *ptr,
PropertyRNA *prop,
const short operation,
const int index,
const bool strict,
bool *r_strict,
bool *r_created)
{
if (r_created != NULL) {
*r_created = false;
}
IDOverrideLibraryProperty *op = RNA_property_override_property_get(bmain, ptr, prop, NULL);
if (!op) {
return NULL;
}
return BKE_lib_override_library_property_operation_get(
op, operation, NULL, NULL, index, index, strict, r_strict, r_created);
}
eRNAOverrideStatus RNA_property_override_library_status(Main *bmain,
PointerRNA *ptr,
PropertyRNA *prop,
const int index)
{
uint override_status = 0;
if (!ptr || !prop || !ptr->owner_id || !ID_IS_OVERRIDE_LIBRARY(ptr->owner_id)) {
return override_status;
}
if (RNA_property_overridable_get(ptr, prop) && RNA_property_editable_flag(ptr, prop)) {
override_status |= RNA_OVERRIDE_STATUS_OVERRIDABLE;
}
IDOverrideLibraryPropertyOperation *opop = RNA_property_override_property_operation_find(
bmain, ptr, prop, index, false, NULL);
if (opop != NULL) {
override_status |= RNA_OVERRIDE_STATUS_OVERRIDDEN;
if (opop->flag & IDOVERRIDE_LIBRARY_FLAG_MANDATORY) {
override_status |= RNA_OVERRIDE_STATUS_MANDATORY;
}
if (opop->flag & IDOVERRIDE_LIBRARY_FLAG_LOCKED) {
override_status |= RNA_OVERRIDE_STATUS_LOCKED;
}
}
return override_status;
}