IDProps: Add 'static type' option to IDProperties.
This implements (most of) the proposal in #122743: * Add a new `IDP_FLAG_STATIC_TYPE` IDProperty flag. * Update `BPy_IDProperty_Map_ValidateAndCreate` and related to never change an existing property type if statically typed. The biggest change happens in bpy assignement code, since instead of replacing the old exisitng property by a newly created one, and copying over a few settings, now the old property is kept if possible, and a new one is only created if needed. And in case the existing property is statically typed, if it cannot be re-used to store the given value, and error is reported and it remains unchanged. `IDP_ARRAY` is also supported for basic numeric types, so 'vector' properties and such work as expected. Lentgh is considered as part of the static type (i.e. one can only assign a 3 components py sequence to a 3-len array property, etc.). Such in-place update is not yet implemented for `IDP_IDPARRAY` and `IDP_GROUP` types. While important (especially the group one), they are not that critical for the current issues related to changing IDProperty types.
This commit is contained in:
parent
a6d3feda8e
commit
4d1fe98604
@ -10,6 +10,8 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "DNA_ID_enums.h"
|
||||
|
||||
#include "BLI_compiler_attrs.h"
|
||||
#include "BLI_function_ref.hh"
|
||||
#include "BLI_span.hh"
|
||||
@ -83,9 +85,12 @@ void IDP_FreeArray(IDProperty *prop);
|
||||
*/
|
||||
IDProperty *IDP_NewStringMaxSize(const char *st,
|
||||
size_t st_maxncpy,
|
||||
const char *name) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(3);
|
||||
IDProperty *IDP_NewString(const char *st, const char *name) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL(2);
|
||||
const char *name,
|
||||
eIDPropertyFlag flags = {}) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL(3);
|
||||
IDProperty *IDP_NewString(const char *st,
|
||||
const char *name,
|
||||
eIDPropertyFlag flags = {}) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(2);
|
||||
/**
|
||||
* \param st: The string to assign.
|
||||
* Doesn't need to be null terminated when clamped by `maxncpy`.
|
||||
@ -236,7 +241,8 @@ bool IDP_EqualsProperties(const IDProperty *prop1,
|
||||
*/
|
||||
IDProperty *IDP_New(char type,
|
||||
const IDPropertyTemplate *val,
|
||||
const char *name) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
const char *name,
|
||||
eIDPropertyFlag flags = {}) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
|
||||
/**
|
||||
* \note This will free allocated data, all child properties of arrays and groups, and unlink IDs!
|
||||
@ -390,23 +396,34 @@ class IDPropertyDeleter {
|
||||
};
|
||||
|
||||
/** \brief Allocate a new IDProperty of type IDP_BOOLEAN, set its name and value. */
|
||||
std::unique_ptr<IDProperty, IDPropertyDeleter> create_bool(StringRefNull prop_name, bool value);
|
||||
std::unique_ptr<IDProperty, IDPropertyDeleter> create_bool(StringRefNull prop_name,
|
||||
bool value,
|
||||
eIDPropertyFlag flags = {});
|
||||
|
||||
/** \brief Allocate a new IDProperty of type IDP_INT, set its name and value. */
|
||||
std::unique_ptr<IDProperty, IDPropertyDeleter> create(StringRefNull prop_name, int32_t value);
|
||||
std::unique_ptr<IDProperty, IDPropertyDeleter> create(StringRefNull prop_name,
|
||||
int32_t value,
|
||||
eIDPropertyFlag flags = {});
|
||||
|
||||
/** \brief Allocate a new IDProperty of type IDP_FLOAT, set its name and value. */
|
||||
std::unique_ptr<IDProperty, IDPropertyDeleter> create(StringRefNull prop_name, float value);
|
||||
std::unique_ptr<IDProperty, IDPropertyDeleter> create(StringRefNull prop_name,
|
||||
float value,
|
||||
eIDPropertyFlag flags = {});
|
||||
|
||||
/** \brief Allocate a new IDProperty of type IDP_DOUBLE, set its name and value. */
|
||||
std::unique_ptr<IDProperty, IDPropertyDeleter> create(StringRefNull prop_name, double value);
|
||||
std::unique_ptr<IDProperty, IDPropertyDeleter> create(StringRefNull prop_name,
|
||||
double value,
|
||||
eIDPropertyFlag flags = {});
|
||||
|
||||
/** \brief Allocate a new IDProperty of type IDP_STRING, set its name and value. */
|
||||
std::unique_ptr<IDProperty, IDPropertyDeleter> create(StringRefNull prop_name,
|
||||
const StringRefNull value);
|
||||
const StringRefNull value,
|
||||
eIDPropertyFlag flags = {});
|
||||
|
||||
/** \brief Allocate a new IDProperty of type IDP_ID, set its name and value. */
|
||||
std::unique_ptr<IDProperty, IDPropertyDeleter> create(StringRefNull prop_name, ID *value);
|
||||
std::unique_ptr<IDProperty, IDPropertyDeleter> create(StringRefNull prop_name,
|
||||
ID *value,
|
||||
eIDPropertyFlag flags = {});
|
||||
|
||||
/**
|
||||
* \brief Allocate a new IDProperty of type IDP_ARRAY and sub-type IDP_INT.
|
||||
@ -414,14 +431,17 @@ std::unique_ptr<IDProperty, IDPropertyDeleter> create(StringRefNull prop_name, I
|
||||
* \param values: The values will be copied into the IDProperty.
|
||||
*/
|
||||
std::unique_ptr<IDProperty, IDPropertyDeleter> create(StringRefNull prop_name,
|
||||
Span<int32_t> values);
|
||||
Span<int32_t> values,
|
||||
eIDPropertyFlag flags = {});
|
||||
|
||||
/**
|
||||
* \brief Allocate a new IDProperty of type IDP_ARRAY and sub-type IDP_FLOAT.
|
||||
*
|
||||
* \param values: The values will be copied into the IDProperty.
|
||||
*/
|
||||
std::unique_ptr<IDProperty, IDPropertyDeleter> create(StringRefNull prop_name, Span<float> values);
|
||||
std::unique_ptr<IDProperty, IDPropertyDeleter> create(StringRefNull prop_name,
|
||||
Span<float> values,
|
||||
eIDPropertyFlag flags = {});
|
||||
|
||||
/**
|
||||
* \brief Allocate a new IDProperty of type IDP_ARRAY and sub-type IDP_DOUBLE.
|
||||
@ -429,7 +449,8 @@ std::unique_ptr<IDProperty, IDPropertyDeleter> create(StringRefNull prop_name, S
|
||||
* \param values: The values will be copied into the IDProperty.
|
||||
*/
|
||||
std::unique_ptr<IDProperty, IDPropertyDeleter> create(StringRefNull prop_name,
|
||||
Span<double> values);
|
||||
Span<double> values,
|
||||
eIDPropertyFlag flags = {});
|
||||
|
||||
/**
|
||||
* \brief Allocate a new IDProperty of type IDP_GROUP.
|
||||
@ -437,6 +458,7 @@ std::unique_ptr<IDProperty, IDPropertyDeleter> create(StringRefNull prop_name,
|
||||
* \param prop_name: The name of the newly created property.
|
||||
*/
|
||||
|
||||
std::unique_ptr<IDProperty, IDPropertyDeleter> create_group(StringRefNull prop_name);
|
||||
std::unique_ptr<IDProperty, IDPropertyDeleter> create_group(StringRefNull prop_name,
|
||||
eIDPropertyFlag flags = {});
|
||||
|
||||
} // namespace blender::bke::idprop
|
||||
|
@ -354,7 +354,10 @@ static IDProperty *IDP_CopyArray(const IDProperty *prop, const int flag)
|
||||
/** \name String Functions (IDProperty String API)
|
||||
* \{ */
|
||||
|
||||
IDProperty *IDP_NewStringMaxSize(const char *st, const size_t st_maxncpy, const char *name)
|
||||
IDProperty *IDP_NewStringMaxSize(const char *st,
|
||||
const size_t st_maxncpy,
|
||||
const char *name,
|
||||
const eIDPropertyFlag flags)
|
||||
{
|
||||
IDProperty *prop = static_cast<IDProperty *>(
|
||||
MEM_callocN(sizeof(IDProperty), "IDProperty string"));
|
||||
@ -382,13 +385,14 @@ IDProperty *IDP_NewStringMaxSize(const char *st, const size_t st_maxncpy, const
|
||||
|
||||
prop->type = IDP_STRING;
|
||||
STRNCPY(prop->name, name);
|
||||
prop->flag = short(flags);
|
||||
|
||||
return prop;
|
||||
}
|
||||
|
||||
IDProperty *IDP_NewString(const char *st, const char *name)
|
||||
IDProperty *IDP_NewString(const char *st, const char *name, const eIDPropertyFlag flags)
|
||||
{
|
||||
return IDP_NewStringMaxSize(st, 0, name);
|
||||
return IDP_NewStringMaxSize(st, 0, name, flags);
|
||||
}
|
||||
|
||||
static IDProperty *IDP_CopyString(const IDProperty *prop, const int flag)
|
||||
@ -978,7 +982,10 @@ bool IDP_EqualsProperties(const IDProperty *prop1, const IDProperty *prop2)
|
||||
return IDP_EqualsProperties_ex(prop1, prop2, true);
|
||||
}
|
||||
|
||||
IDProperty *IDP_New(const char type, const IDPropertyTemplate *val, const char *name)
|
||||
IDProperty *IDP_New(const char type,
|
||||
const IDPropertyTemplate *val,
|
||||
const char *name,
|
||||
const eIDPropertyFlag flags)
|
||||
{
|
||||
IDProperty *prop = nullptr;
|
||||
|
||||
@ -1074,6 +1081,7 @@ IDProperty *IDP_New(const char type, const IDPropertyTemplate *val, const char *
|
||||
|
||||
prop->type = type;
|
||||
STRNCPY(prop->name, name);
|
||||
prop->flag = short(flags);
|
||||
|
||||
return prop;
|
||||
}
|
||||
|
@ -14,62 +14,73 @@ namespace blender::bke::idprop {
|
||||
/** \name Create Functions
|
||||
* \{ */
|
||||
|
||||
std::unique_ptr<IDProperty, IDPropertyDeleter> create(const StringRefNull prop_name, int32_t value)
|
||||
std::unique_ptr<IDProperty, IDPropertyDeleter> create(const StringRefNull prop_name,
|
||||
int32_t value,
|
||||
const eIDPropertyFlag flags)
|
||||
{
|
||||
IDPropertyTemplate prop_template{0};
|
||||
prop_template.i = value;
|
||||
IDProperty *property = IDP_New(IDP_INT, &prop_template, prop_name.c_str());
|
||||
IDProperty *property = IDP_New(IDP_INT, &prop_template, prop_name.c_str(), flags);
|
||||
return std::unique_ptr<IDProperty, IDPropertyDeleter>(property);
|
||||
}
|
||||
|
||||
std::unique_ptr<IDProperty, IDPropertyDeleter> create_bool(const StringRefNull prop_name,
|
||||
bool value)
|
||||
bool value,
|
||||
const eIDPropertyFlag flags)
|
||||
{
|
||||
IDPropertyTemplate prop_template{0};
|
||||
prop_template.i = value;
|
||||
IDProperty *property = IDP_New(IDP_BOOLEAN, &prop_template, prop_name.c_str());
|
||||
return std::unique_ptr<IDProperty, IDPropertyDeleter>(property);
|
||||
}
|
||||
|
||||
std::unique_ptr<IDProperty, IDPropertyDeleter> create(const StringRefNull prop_name, float value)
|
||||
{
|
||||
IDPropertyTemplate prop_template{0};
|
||||
prop_template.f = value;
|
||||
IDProperty *property = IDP_New(IDP_FLOAT, &prop_template, prop_name.c_str());
|
||||
return std::unique_ptr<IDProperty, IDPropertyDeleter>(property);
|
||||
}
|
||||
|
||||
std::unique_ptr<IDProperty, IDPropertyDeleter> create(const StringRefNull prop_name, double value)
|
||||
{
|
||||
IDPropertyTemplate prop_template{0};
|
||||
prop_template.d = value;
|
||||
IDProperty *property = IDP_New(IDP_DOUBLE, &prop_template, prop_name.c_str());
|
||||
IDProperty *property = IDP_New(IDP_BOOLEAN, &prop_template, prop_name.c_str(), flags);
|
||||
return std::unique_ptr<IDProperty, IDPropertyDeleter>(property);
|
||||
}
|
||||
|
||||
std::unique_ptr<IDProperty, IDPropertyDeleter> create(const StringRefNull prop_name,
|
||||
const StringRefNull value)
|
||||
float value,
|
||||
const eIDPropertyFlag flags)
|
||||
{
|
||||
IDProperty *property = IDP_NewString(value.c_str(), prop_name.c_str());
|
||||
IDPropertyTemplate prop_template{0};
|
||||
prop_template.f = value;
|
||||
IDProperty *property = IDP_New(IDP_FLOAT, &prop_template, prop_name.c_str(), flags);
|
||||
return std::unique_ptr<IDProperty, IDPropertyDeleter>(property);
|
||||
}
|
||||
|
||||
std::unique_ptr<IDProperty, IDPropertyDeleter> create(const StringRefNull prop_name, ID *value)
|
||||
std::unique_ptr<IDProperty, IDPropertyDeleter> create(const StringRefNull prop_name,
|
||||
double value,
|
||||
const eIDPropertyFlag flags)
|
||||
{
|
||||
IDPropertyTemplate prop_template{0};
|
||||
prop_template.d = value;
|
||||
IDProperty *property = IDP_New(IDP_DOUBLE, &prop_template, prop_name.c_str(), flags);
|
||||
return std::unique_ptr<IDProperty, IDPropertyDeleter>(property);
|
||||
}
|
||||
|
||||
std::unique_ptr<IDProperty, IDPropertyDeleter> create(const StringRefNull prop_name,
|
||||
const StringRefNull value,
|
||||
const eIDPropertyFlag flags)
|
||||
{
|
||||
IDProperty *property = IDP_NewString(value.c_str(), prop_name.c_str(), flags);
|
||||
return std::unique_ptr<IDProperty, IDPropertyDeleter>(property);
|
||||
}
|
||||
|
||||
std::unique_ptr<IDProperty, IDPropertyDeleter> create(const StringRefNull prop_name,
|
||||
ID *value,
|
||||
const eIDPropertyFlag flags)
|
||||
{
|
||||
IDPropertyTemplate prop_template{0};
|
||||
prop_template.id = value;
|
||||
IDProperty *property = IDP_New(IDP_ID, &prop_template, prop_name.c_str());
|
||||
IDProperty *property = IDP_New(IDP_ID, &prop_template, prop_name.c_str(), flags);
|
||||
return std::unique_ptr<IDProperty, IDPropertyDeleter>(property);
|
||||
}
|
||||
|
||||
static std::unique_ptr<IDProperty, IDPropertyDeleter> array_create(const StringRefNull prop_name,
|
||||
eIDPropertyType subtype,
|
||||
size_t array_len)
|
||||
size_t array_len,
|
||||
const eIDPropertyFlag flags)
|
||||
{
|
||||
IDPropertyTemplate prop_template{0};
|
||||
prop_template.array.len = array_len;
|
||||
prop_template.array.type = subtype;
|
||||
IDProperty *property = IDP_New(IDP_ARRAY, &prop_template, prop_name.c_str());
|
||||
IDProperty *property = IDP_New(IDP_ARRAY, &prop_template, prop_name.c_str(), flags);
|
||||
return std::unique_ptr<IDProperty, IDPropertyDeleter>(property);
|
||||
}
|
||||
|
||||
@ -92,7 +103,8 @@ template<
|
||||
/** Sub-type of the #ID_ARRAY. Must match #PrimitiveType. */
|
||||
eIDPropertyType id_property_subtype>
|
||||
std::unique_ptr<IDProperty, IDPropertyDeleter> create_array(StringRefNull prop_name,
|
||||
Span<PrimitiveType> values)
|
||||
Span<PrimitiveType> values,
|
||||
const eIDPropertyFlag flags)
|
||||
{
|
||||
static_assert(std::is_same_v<PrimitiveType, int32_t> || std::is_same_v<PrimitiveType, float> ||
|
||||
std::is_same_v<PrimitiveType, double>,
|
||||
@ -107,34 +119,38 @@ std::unique_ptr<IDProperty, IDPropertyDeleter> create_array(StringRefNull prop_n
|
||||
const int64_t values_len = values.size();
|
||||
BLI_assert(values_len > 0);
|
||||
std::unique_ptr<IDProperty, IDPropertyDeleter> property = array_create(
|
||||
prop_name.c_str(), id_property_subtype, values_len);
|
||||
prop_name.c_str(), id_property_subtype, values_len, flags);
|
||||
array_values_set(
|
||||
property.get(), static_cast<const void *>(values.data()), values_len, sizeof(PrimitiveType));
|
||||
return property;
|
||||
}
|
||||
|
||||
std::unique_ptr<IDProperty, IDPropertyDeleter> create(const StringRefNull prop_name,
|
||||
Span<int32_t> values)
|
||||
Span<int32_t> values,
|
||||
const eIDPropertyFlag flags)
|
||||
{
|
||||
return create_array<int32_t, IDP_INT>(prop_name, values);
|
||||
return create_array<int32_t, IDP_INT>(prop_name, values, flags);
|
||||
}
|
||||
|
||||
std::unique_ptr<IDProperty, IDPropertyDeleter> create(const StringRefNull prop_name,
|
||||
Span<float> values)
|
||||
Span<float> values,
|
||||
const eIDPropertyFlag flags)
|
||||
{
|
||||
return create_array<float, IDP_FLOAT>(prop_name, values);
|
||||
return create_array<float, IDP_FLOAT>(prop_name, values, flags);
|
||||
}
|
||||
|
||||
std::unique_ptr<IDProperty, IDPropertyDeleter> create(const StringRefNull prop_name,
|
||||
Span<double> values)
|
||||
Span<double> values,
|
||||
const eIDPropertyFlag flags)
|
||||
{
|
||||
return create_array<double, IDP_DOUBLE>(prop_name, values);
|
||||
return create_array<double, IDP_DOUBLE>(prop_name, values, flags);
|
||||
}
|
||||
|
||||
std::unique_ptr<IDProperty, IDPropertyDeleter> create_group(const StringRefNull prop_name)
|
||||
std::unique_ptr<IDProperty, IDPropertyDeleter> create_group(const StringRefNull prop_name,
|
||||
const eIDPropertyFlag flags)
|
||||
{
|
||||
IDPropertyTemplate prop_template{0};
|
||||
IDProperty *property = IDP_New(IDP_GROUP, &prop_template, prop_name.c_str());
|
||||
IDProperty *property = IDP_New(IDP_GROUP, &prop_template, prop_name.c_str(), flags);
|
||||
return std::unique_ptr<IDProperty, IDPropertyDeleter>(property);
|
||||
}
|
||||
|
||||
|
@ -71,6 +71,19 @@ typedef enum eIDPropertyFlag {
|
||||
* local-inserted ones, as many operations are not allowed on the former.
|
||||
*/
|
||||
IDP_FLAG_OVERRIDELIBRARY_LOCAL = 1 << 1,
|
||||
/**
|
||||
* This #IDProperty has a static type, i.e. its #eIDPropertyType cannot be changed by assigning a
|
||||
* new value to it.
|
||||
*
|
||||
* Currently, array len is also considered as fixed (i.e. part of the type) when this flag is
|
||||
* set. This allows to avoid IDProperty storing vectors e.g. to see their length modified.
|
||||
*
|
||||
* \note Currently, all overridable IDProp are also statically typed. IDProps used as storage for
|
||||
* dynamic RNA properties are also always dynamically typed.
|
||||
*
|
||||
* \note Internal flag, user have no direct way to define or edit it.
|
||||
*/
|
||||
IDP_FLAG_STATIC_TYPE = 1 << 4,
|
||||
/**
|
||||
* This means the property is set but RNA will return false when checking
|
||||
* #RNA_property_is_set, currently this is a runtime flag.
|
||||
|
@ -341,7 +341,9 @@ static char idp_sequence_type(PyObject *seq_fast)
|
||||
for (i = 0; i < len; i++) {
|
||||
item = seq_fast_items[i];
|
||||
if (PyFloat_Check(item)) {
|
||||
if (type == IDP_IDPARRAY) { /* mixed dict/int */
|
||||
/* Mixed float and any other type but int.
|
||||
* NOTE: Mixed float/int is allowed, and considered as float values. */
|
||||
if (!ELEM(type, IDP_INT, IDP_DOUBLE)) {
|
||||
return -1;
|
||||
}
|
||||
type = IDP_DOUBLE;
|
||||
@ -354,7 +356,9 @@ static char idp_sequence_type(PyObject *seq_fast)
|
||||
type = IDP_BOOLEAN;
|
||||
}
|
||||
else if (PyLong_Check(item)) {
|
||||
if (type == IDP_IDPARRAY) { /* mixed dict/int */
|
||||
/* Mixed int and any other type but float.
|
||||
* NOTE: Mixed float/int is allowed, and considered as float values. */
|
||||
if (!ELEM(type, IDP_INT, IDP_DOUBLE)) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@ -408,53 +412,206 @@ static const char *idp_try_read_name(PyObject *name_obj)
|
||||
/**
|
||||
* The 'idp_from_Py*' functions expect that the input type has been checked before
|
||||
* and return nullptr if the IDProperty can't be created.
|
||||
*
|
||||
* \param prop_exist If not null, attempt to assign given `ob` value to this property first, and
|
||||
* only create a new one if not possible.
|
||||
* If no assignment (or conversion and assignment) is possible, the current
|
||||
* value remains unchanged.
|
||||
*
|
||||
* \param do_conversion If `true`, allow some 'reasonable' conversion of input value to match the
|
||||
* `prop_exist` property type. E.g. can convert an `int` to a `float`, but not
|
||||
* the other way around.
|
||||
*
|
||||
* \param can_create Whether creating a new IDProperty is allowed.
|
||||
*
|
||||
* \return `prop_exist` if given and it could be assigned given `ob` value, a new IDProperty
|
||||
* otherwise.
|
||||
*/
|
||||
|
||||
static IDProperty *idp_from_PyFloat(const char *name, PyObject *ob)
|
||||
static IDProperty *idp_from_PyFloat(IDProperty *prop_exist,
|
||||
const char *name,
|
||||
PyObject *ob,
|
||||
const bool do_conversion,
|
||||
const bool can_create)
|
||||
{
|
||||
return blender::bke::idprop::create(name, PyFloat_AsDouble(ob)).release();
|
||||
}
|
||||
|
||||
static IDProperty *idp_from_PyBool(const char *name, PyObject *ob)
|
||||
{
|
||||
return blender::bke::idprop::create_bool(name, PyC_Long_AsBool(ob)).release();
|
||||
}
|
||||
|
||||
static IDProperty *idp_from_PyLong(const char *name, PyObject *ob)
|
||||
{
|
||||
const int value = PyC_Long_AsI32(ob);
|
||||
if (value == -1 && PyErr_Occurred()) {
|
||||
return nullptr;
|
||||
IDProperty *prop = nullptr;
|
||||
const double value = PyFloat_AsDouble(ob);
|
||||
if (prop_exist) {
|
||||
if (prop_exist->type == IDP_DOUBLE) {
|
||||
IDP_Double(prop_exist) = value;
|
||||
prop = prop_exist;
|
||||
}
|
||||
else if (do_conversion) {
|
||||
switch (prop_exist->type) {
|
||||
case IDP_FLOAT:
|
||||
IDP_Float(prop_exist) = float(value);
|
||||
prop = prop_exist;
|
||||
break;
|
||||
case IDP_STRING:
|
||||
case IDP_INT:
|
||||
case IDP_ARRAY:
|
||||
case IDP_GROUP:
|
||||
case IDP_ID:
|
||||
case IDP_DOUBLE:
|
||||
case IDP_IDPARRAY:
|
||||
case IDP_BOOLEAN:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!prop && can_create) {
|
||||
prop = blender::bke::idprop::create(name, value).release();
|
||||
}
|
||||
return blender::bke::idprop::create(name, value).release();
|
||||
}
|
||||
|
||||
static IDProperty *idp_from_PyUnicode(const char *name, PyObject *ob)
|
||||
{
|
||||
IDProperty *prop;
|
||||
IDPropertyTemplate val = {0};
|
||||
#ifdef USE_STRING_COERCE
|
||||
Py_ssize_t value_len;
|
||||
PyObject *value_coerce = nullptr;
|
||||
val.string.str = PyC_UnicodeAsBytesAndSize(ob, &value_len, &value_coerce);
|
||||
val.string.len = int(value_len) + 1;
|
||||
val.string.subtype = IDP_STRING_SUB_UTF8;
|
||||
prop = IDP_New(IDP_STRING, &val, name);
|
||||
Py_XDECREF(value_coerce);
|
||||
#else
|
||||
val.str = PyUnicode_AsUTF8(ob);
|
||||
prop = IDP_New(IDP_STRING, val, name);
|
||||
#endif
|
||||
return prop;
|
||||
}
|
||||
|
||||
static IDProperty *idp_from_PyBytes(const char *name, PyObject *ob)
|
||||
static IDProperty *idp_from_PyBool(IDProperty *prop_exist,
|
||||
const char *name,
|
||||
PyObject *ob,
|
||||
const bool do_conversion,
|
||||
const bool can_create)
|
||||
{
|
||||
IDPropertyTemplate val = {0};
|
||||
val.string.str = PyBytes_AS_STRING(ob);
|
||||
val.string.len = PyBytes_GET_SIZE(ob);
|
||||
val.string.subtype = IDP_STRING_SUB_BYTE;
|
||||
return IDP_New(IDP_STRING, &val, name);
|
||||
IDProperty *prop = nullptr;
|
||||
const bool value = PyC_Long_AsBool(ob);
|
||||
if (prop_exist) {
|
||||
if (prop_exist->type == IDP_BOOLEAN) {
|
||||
IDP_Bool(prop_exist) = value;
|
||||
prop = prop_exist;
|
||||
}
|
||||
else if (do_conversion) {
|
||||
switch (prop_exist->type) {
|
||||
case IDP_INT:
|
||||
IDP_Int(prop_exist) = int(value);
|
||||
prop = prop_exist;
|
||||
break;
|
||||
case IDP_STRING:
|
||||
case IDP_FLOAT:
|
||||
case IDP_ARRAY:
|
||||
case IDP_GROUP:
|
||||
case IDP_ID:
|
||||
case IDP_DOUBLE:
|
||||
case IDP_IDPARRAY:
|
||||
case IDP_BOOLEAN:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!prop && can_create) {
|
||||
prop = blender::bke::idprop::create_bool(name, value).release();
|
||||
}
|
||||
return prop;
|
||||
}
|
||||
|
||||
static IDProperty *idp_from_PyLong(IDProperty *prop_exist,
|
||||
const char *name,
|
||||
PyObject *ob,
|
||||
const bool do_conversion,
|
||||
const bool can_create)
|
||||
{
|
||||
IDProperty *prop = nullptr;
|
||||
const int value = PyC_Long_AsI32(ob);
|
||||
if (value == -1 && PyErr_Occurred()) {
|
||||
return prop;
|
||||
}
|
||||
if (prop_exist) {
|
||||
if (prop_exist->type == IDP_INT) {
|
||||
IDP_Int(prop_exist) = value;
|
||||
prop = prop_exist;
|
||||
}
|
||||
else if (do_conversion) {
|
||||
switch (prop_exist->type) {
|
||||
case IDP_FLOAT:
|
||||
IDP_Float(prop_exist) = float(value);
|
||||
prop = prop_exist;
|
||||
break;
|
||||
case IDP_DOUBLE:
|
||||
IDP_Double(prop_exist) = double(value);
|
||||
prop = prop_exist;
|
||||
break;
|
||||
case IDP_STRING:
|
||||
case IDP_INT:
|
||||
case IDP_ARRAY:
|
||||
case IDP_GROUP:
|
||||
case IDP_ID:
|
||||
case IDP_IDPARRAY:
|
||||
case IDP_BOOLEAN:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!prop && can_create) {
|
||||
prop = blender::bke::idprop::create(name, value).release();
|
||||
}
|
||||
return prop;
|
||||
}
|
||||
|
||||
static IDProperty *idp_from_PyUnicode(IDProperty *prop_exist,
|
||||
const char *name,
|
||||
PyObject *ob,
|
||||
const bool /*do_conversion*/,
|
||||
const bool can_create)
|
||||
{
|
||||
IDProperty *prop = nullptr;
|
||||
Py_ssize_t value_len = 0;
|
||||
const char *value = nullptr;
|
||||
|
||||
#ifdef USE_STRING_COERCE
|
||||
PyObject *value_coerce = nullptr;
|
||||
value = PyC_UnicodeAsBytesAndSize(ob, &value_len, &value_coerce);
|
||||
#else
|
||||
value = PyUnicode_AsUTF8AndSize(ob, &value_len);
|
||||
#endif
|
||||
|
||||
if (prop_exist) {
|
||||
if (prop_exist->type == IDP_STRING && prop_exist->subtype == IDP_STRING_SUB_UTF8) {
|
||||
IDP_AssignStringMaxSize(prop_exist, value, int(value_len) + 1);
|
||||
prop = prop_exist;
|
||||
}
|
||||
/* No conversion. */
|
||||
}
|
||||
|
||||
if (!prop && can_create) {
|
||||
IDPropertyTemplate val = {0};
|
||||
val.string.str = value;
|
||||
val.string.len = int(value_len) + 1;
|
||||
val.string.subtype = IDP_STRING_SUB_UTF8;
|
||||
prop = IDP_New(IDP_STRING, &val, name);
|
||||
}
|
||||
|
||||
#ifdef USE_STRING_COERCE
|
||||
Py_XDECREF(value_coerce);
|
||||
#endif
|
||||
|
||||
return prop;
|
||||
}
|
||||
|
||||
static IDProperty *idp_from_PyBytes(IDProperty *prop_exist,
|
||||
const char *name,
|
||||
PyObject *ob,
|
||||
const bool /*do_conversion*/,
|
||||
const bool can_create)
|
||||
{
|
||||
IDProperty *prop = nullptr;
|
||||
Py_ssize_t value_len = PyBytes_GET_SIZE(ob);
|
||||
const char *value = PyBytes_AS_STRING(ob);
|
||||
|
||||
if (prop_exist) {
|
||||
if (prop_exist->type == IDP_STRING && prop_exist->subtype == IDP_STRING_SUB_BYTE) {
|
||||
IDP_AssignStringMaxSize(prop_exist, value, int(value_len) + 1);
|
||||
prop = prop_exist;
|
||||
}
|
||||
/* No conversion. */
|
||||
}
|
||||
|
||||
if (!prop && can_create) {
|
||||
IDPropertyTemplate val = {0};
|
||||
val.string.str = value;
|
||||
val.string.len = int(value_len);
|
||||
val.string.subtype = IDP_STRING_SUB_BYTE;
|
||||
prop = IDP_New(IDP_STRING, &val, name);
|
||||
}
|
||||
|
||||
return prop;
|
||||
}
|
||||
|
||||
static int idp_array_type_from_formatstr_and_size(const char *typestr, Py_ssize_t itemsize)
|
||||
@ -496,28 +653,41 @@ static const char *idp_format_from_array_type(int type)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static IDProperty *idp_from_PySequence_Buffer(const char *name, Py_buffer *buffer)
|
||||
static IDProperty *idp_from_PySequence_Buffer(IDProperty *prop_exist,
|
||||
const char *name,
|
||||
Py_buffer &buffer,
|
||||
const int idp_type,
|
||||
const bool /*do_conversion*/,
|
||||
const bool can_create)
|
||||
{
|
||||
IDProperty *prop;
|
||||
IDPropertyTemplate val = {0};
|
||||
BLI_assert(idp_type != -1);
|
||||
IDProperty *prop = nullptr;
|
||||
|
||||
const int id_type = idp_array_type_from_formatstr_and_size(buffer->format, buffer->itemsize);
|
||||
if (id_type == -1) {
|
||||
/* should never happen as the type has been checked before */
|
||||
return nullptr;
|
||||
if (prop_exist) {
|
||||
if (prop_exist->type == IDP_ARRAY && prop_exist->subtype == idp_type) {
|
||||
BLI_assert(buffer.len == prop_exist->len);
|
||||
memcpy(IDP_Array(prop_exist), buffer.buf, buffer.len);
|
||||
prop = prop_exist;
|
||||
}
|
||||
/* No conversion. */
|
||||
}
|
||||
if (!prop && can_create) {
|
||||
IDPropertyTemplate val = {0};
|
||||
val.array.type = idp_type;
|
||||
val.array.len = buffer.len / buffer.itemsize;
|
||||
prop = IDP_New(IDP_ARRAY, &val, name);
|
||||
memcpy(IDP_Array(prop), buffer.buf, buffer.len);
|
||||
}
|
||||
|
||||
val.array.type = id_type;
|
||||
val.array.len = buffer->len / buffer->itemsize;
|
||||
|
||||
prop = IDP_New(IDP_ARRAY, &val, name);
|
||||
memcpy(IDP_Array(prop), buffer->buf, buffer->len);
|
||||
return prop;
|
||||
}
|
||||
|
||||
static IDProperty *idp_from_PySequence_Fast(const char *name, PyObject *ob)
|
||||
static IDProperty *idp_from_PySequence_Fast(IDProperty *prop_exist,
|
||||
const char *name,
|
||||
PyObject *ob,
|
||||
const bool do_conversion,
|
||||
const bool can_create)
|
||||
{
|
||||
IDProperty *prop;
|
||||
IDProperty *prop = nullptr;
|
||||
IDPropertyTemplate val = {0};
|
||||
|
||||
PyObject **ob_seq_fast_items;
|
||||
@ -528,18 +698,115 @@ static IDProperty *idp_from_PySequence_Fast(const char *name, PyObject *ob)
|
||||
|
||||
/* IDProperties do not support mixed type of data in an array. Try to extract a single type from
|
||||
* the whole sequence, or error. */
|
||||
if ((val.array.type = idp_sequence_type(ob)) == char(-1)) {
|
||||
val.array.type = idp_sequence_type(ob);
|
||||
if (val.array.type == char(-1)) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"only floats, ints, booleans and dicts are allowed in ID property arrays");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* validate sequence and derive type.
|
||||
* we assume IDP_INT unless we hit a float
|
||||
* number; then we assume it's */
|
||||
if (!ELEM(val.array.type, IDP_DOUBLE, IDP_INT, IDP_BOOLEAN, IDP_IDPARRAY)) {
|
||||
/* Should never happen. */
|
||||
PyErr_SetString(PyExc_RuntimeError, "internal error with idp array.type");
|
||||
BLI_assert_unreachable();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
val.array.len = PySequence_Fast_GET_SIZE(ob);
|
||||
|
||||
/* NOTE: For now do not consider resizing existing array property. Also do not handle IDPARRAY.
|
||||
* - 'static type' also means 'fixed length' (e.g. vectors or matrices cases).
|
||||
* - For 'dynamic type' case, it's not really a problem if array properties get replaced
|
||||
* currently.
|
||||
*/
|
||||
if (prop_exist && prop_exist->len == val.array.len) {
|
||||
switch (val.array.type) {
|
||||
case IDP_DOUBLE: {
|
||||
const bool to_float = (prop_exist->subtype == IDP_FLOAT);
|
||||
if (!(prop_exist->type == IDP_ARRAY &&
|
||||
(prop_exist->subtype == IDP_DOUBLE || (do_conversion && to_float))))
|
||||
{
|
||||
break;
|
||||
}
|
||||
prop = prop_exist;
|
||||
void *prop_data = IDP_Array(prop);
|
||||
for (i = 0; i < val.array.len; i++) {
|
||||
item = ob_seq_fast_items[i];
|
||||
const double value = PyFloat_AsDouble(item);
|
||||
if ((value == -1.0) && PyErr_Occurred()) {
|
||||
continue;
|
||||
}
|
||||
if (to_float) {
|
||||
static_cast<float *>(prop_data)[i] = float(value);
|
||||
}
|
||||
else {
|
||||
static_cast<double *>(prop_data)[i] = value;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IDP_INT: {
|
||||
const bool to_float = (prop_exist->subtype == IDP_FLOAT);
|
||||
const bool to_double = (prop_exist->subtype == IDP_DOUBLE);
|
||||
if (!(prop_exist->type == IDP_ARRAY &&
|
||||
(prop_exist->subtype == IDP_INT || (do_conversion && (to_float || to_double)))))
|
||||
{
|
||||
break;
|
||||
}
|
||||
prop = prop_exist;
|
||||
void *prop_data = IDP_Array(prop);
|
||||
for (i = 0; i < val.array.len; i++) {
|
||||
item = ob_seq_fast_items[i];
|
||||
const int value = PyC_Long_AsI32(item);
|
||||
if ((value == -1) && PyErr_Occurred()) {
|
||||
continue;
|
||||
}
|
||||
if (to_float) {
|
||||
static_cast<float *>(prop_data)[i] = float(value);
|
||||
}
|
||||
else if (to_double) {
|
||||
static_cast<double *>(prop_data)[i] = double(value);
|
||||
}
|
||||
else {
|
||||
static_cast<int *>(prop_data)[i] = value;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IDP_BOOLEAN: {
|
||||
const bool to_int = (prop_exist->subtype == IDP_INT);
|
||||
if (!(prop_exist->type == IDP_ARRAY &&
|
||||
(prop_exist->subtype == IDP_BOOLEAN || (do_conversion && to_int))))
|
||||
{
|
||||
break;
|
||||
}
|
||||
prop = prop_exist;
|
||||
void *prop_data = IDP_Array(prop);
|
||||
for (i = 0; i < val.array.len; i++) {
|
||||
item = ob_seq_fast_items[i];
|
||||
const int value = PyC_Long_AsBool(item);
|
||||
if ((value == -1) && PyErr_Occurred()) {
|
||||
continue;
|
||||
}
|
||||
if (to_int) {
|
||||
static_cast<int *>(prop_data)[i] = value;
|
||||
}
|
||||
else {
|
||||
static_cast<bool *>(prop_data)[i] = bool(value);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IDP_IDPARRAY: {
|
||||
/* TODO? */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (prop || !can_create) {
|
||||
return prop;
|
||||
}
|
||||
|
||||
switch (val.array.type) {
|
||||
case IDP_DOUBLE: {
|
||||
double *prop_data;
|
||||
@ -592,18 +859,19 @@ static IDProperty *idp_from_PySequence_Fast(const char *name, PyObject *ob)
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
/* should never happen */
|
||||
PyErr_SetString(PyExc_RuntimeError, "internal error with idp array.type");
|
||||
return nullptr;
|
||||
}
|
||||
return prop;
|
||||
}
|
||||
|
||||
static IDProperty *idp_from_PySequence(const char *name, PyObject *ob)
|
||||
static IDProperty *idp_from_PySequence(IDProperty *prop_exist,
|
||||
const char *name,
|
||||
PyObject *ob,
|
||||
const bool do_conversion,
|
||||
const bool can_create)
|
||||
{
|
||||
Py_buffer buffer;
|
||||
bool use_buffer = false;
|
||||
int idp_buffer_type = -1;
|
||||
|
||||
if (PyObject_CheckBuffer(ob)) {
|
||||
if (PyObject_GetBuffer(ob, &buffer, PyBUF_ND | PyBUF_FORMAT) == -1) {
|
||||
@ -612,11 +880,18 @@ static IDProperty *idp_from_PySequence(const char *name, PyObject *ob)
|
||||
PyErr_Clear();
|
||||
}
|
||||
else {
|
||||
const char format = PyC_StructFmt_type_from_str(buffer.format);
|
||||
if (PyC_StructFmt_type_is_float_any(format) ||
|
||||
(PyC_StructFmt_type_is_int_any(format) && buffer.itemsize == 4))
|
||||
{
|
||||
use_buffer = true;
|
||||
idp_buffer_type = idp_array_type_from_formatstr_and_size(buffer.format, buffer.itemsize);
|
||||
if (idp_buffer_type != -1) {
|
||||
/* If creating a new IDProp is not allowed, and the existing one is not usable (same size
|
||||
* and type), then the 'buffer assignment' process cannot be used. */
|
||||
if (!can_create && (!prop_exist || (prop_exist->type != idp_buffer_type) ||
|
||||
(prop_exist->len != buffer.len)))
|
||||
{
|
||||
PyBuffer_Release(&buffer);
|
||||
}
|
||||
else {
|
||||
use_buffer = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
PyBuffer_Release(&buffer);
|
||||
@ -625,14 +900,16 @@ static IDProperty *idp_from_PySequence(const char *name, PyObject *ob)
|
||||
}
|
||||
|
||||
if (use_buffer) {
|
||||
IDProperty *prop = idp_from_PySequence_Buffer(name, &buffer);
|
||||
IDProperty *prop = idp_from_PySequence_Buffer(
|
||||
prop_exist, name, buffer, idp_buffer_type, do_conversion, can_create);
|
||||
PyBuffer_Release(&buffer);
|
||||
return prop;
|
||||
}
|
||||
|
||||
PyObject *ob_seq_fast = PySequence_Fast(ob, "py -> idprop");
|
||||
if (ob_seq_fast != nullptr) {
|
||||
IDProperty *prop = idp_from_PySequence_Fast(name, ob_seq_fast);
|
||||
IDProperty *prop = idp_from_PySequence_Fast(
|
||||
prop_exist, name, ob_seq_fast, do_conversion, can_create);
|
||||
Py_DECREF(ob_seq_fast);
|
||||
return prop;
|
||||
}
|
||||
@ -640,10 +917,16 @@ static IDProperty *idp_from_PySequence(const char *name, PyObject *ob)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static IDProperty *idp_from_PyMapping(const char *name, PyObject *ob)
|
||||
static IDProperty *idp_from_PyMapping(IDProperty * /*prop_exist*/,
|
||||
const char *name,
|
||||
PyObject *ob,
|
||||
const bool /*do_conversion*/,
|
||||
const bool /*can_create*/)
|
||||
{
|
||||
IDProperty *prop;
|
||||
|
||||
/* TODO: Handle editing in-place of existing property (#IDP_FLAG_STATIC_TYPE flag). */
|
||||
|
||||
PyObject *keys, *vals, *key, *pval;
|
||||
int i, len;
|
||||
/* yay! we get into recursive stuff now! */
|
||||
@ -674,43 +957,61 @@ static IDProperty *idp_from_PyMapping(const char *name, PyObject *ob)
|
||||
return prop;
|
||||
}
|
||||
|
||||
static IDProperty *idp_from_DatablockPointer(const char *name, PyObject *ob)
|
||||
static IDProperty *idp_from_DatablockPointer(IDProperty *prop_exist,
|
||||
const char *name,
|
||||
PyObject *ob,
|
||||
const bool /*do_conversion*/,
|
||||
const bool can_create)
|
||||
{
|
||||
ID *id = nullptr;
|
||||
pyrna_id_FromPyObject(ob, &id);
|
||||
return blender::bke::idprop::create(name, id).release();
|
||||
IDProperty *prop = nullptr;
|
||||
ID *value = nullptr;
|
||||
pyrna_id_FromPyObject(ob, &value);
|
||||
if (prop_exist) {
|
||||
if (prop_exist->type == IDP_ID) {
|
||||
IDP_AssignID(prop_exist, value, 0);
|
||||
prop = prop_exist;
|
||||
}
|
||||
/* No conversion. */
|
||||
}
|
||||
if (!prop && can_create) {
|
||||
prop = blender::bke::idprop::create(name, value).release();
|
||||
}
|
||||
return prop;
|
||||
}
|
||||
|
||||
static IDProperty *idp_from_PyObject(PyObject *name_obj, PyObject *ob)
|
||||
static IDProperty *idp_from_PyObject(IDProperty *prop_exist,
|
||||
const char *name,
|
||||
PyObject *ob,
|
||||
const bool do_conversion,
|
||||
const bool can_create)
|
||||
{
|
||||
const char *name = idp_try_read_name(name_obj);
|
||||
if (name == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (PyFloat_Check(ob)) {
|
||||
return idp_from_PyFloat(name, ob);
|
||||
return idp_from_PyFloat(prop_exist, name, ob, do_conversion, can_create);
|
||||
}
|
||||
if (PyBool_Check(ob)) {
|
||||
return idp_from_PyBool(name, ob);
|
||||
return idp_from_PyBool(prop_exist, name, ob, do_conversion, can_create);
|
||||
}
|
||||
if (PyLong_Check(ob)) {
|
||||
return idp_from_PyLong(name, ob);
|
||||
return idp_from_PyLong(prop_exist, name, ob, do_conversion, can_create);
|
||||
}
|
||||
if (PyUnicode_Check(ob)) {
|
||||
return idp_from_PyUnicode(name, ob);
|
||||
return idp_from_PyUnicode(prop_exist, name, ob, do_conversion, can_create);
|
||||
}
|
||||
if (PyBytes_Check(ob)) {
|
||||
return idp_from_PyBytes(name, ob);
|
||||
return idp_from_PyBytes(prop_exist, name, ob, do_conversion, can_create);
|
||||
}
|
||||
if (PySequence_Check(ob)) {
|
||||
return idp_from_PySequence(name, ob);
|
||||
return idp_from_PySequence(prop_exist, name, ob, do_conversion, can_create);
|
||||
}
|
||||
if (ob == Py_None || pyrna_id_CheckPyObject(ob)) {
|
||||
return idp_from_DatablockPointer(name, ob);
|
||||
return idp_from_DatablockPointer(prop_exist, name, ob, do_conversion, can_create);
|
||||
}
|
||||
if (PyMapping_Check(ob)) {
|
||||
return idp_from_PyMapping(name, ob);
|
||||
return idp_from_PyMapping(prop_exist, name, ob, do_conversion, can_create);
|
||||
}
|
||||
|
||||
PyErr_Format(
|
||||
@ -726,55 +1027,72 @@ static IDProperty *idp_from_PyObject(PyObject *name_obj, PyObject *ob)
|
||||
|
||||
bool BPy_IDProperty_Map_ValidateAndCreate(PyObject *name_obj, IDProperty *group, PyObject *ob)
|
||||
{
|
||||
IDProperty *prop = idp_from_PyObject(name_obj, ob);
|
||||
if (prop == nullptr) {
|
||||
const char *name = idp_try_read_name(name_obj);
|
||||
if (!name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* If the container is an array of IDProperties, always add a new property to it. */
|
||||
if (group->type == IDP_IDPARRAY) {
|
||||
IDP_AppendArray(group, prop);
|
||||
IDProperty *new_prop = idp_from_PyObject(nullptr, name, ob, false, true);
|
||||
if (new_prop == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
IDP_AppendArray(group, new_prop);
|
||||
/* IDP_AppendArray does a shallow copy (memcpy), only free memory */
|
||||
MEM_freeN(prop);
|
||||
}
|
||||
else {
|
||||
IDProperty *prop_exist;
|
||||
MEM_freeN(new_prop);
|
||||
|
||||
/* avoid freeing when types match in case they are referenced by the UI, see: #37073
|
||||
* obviously this isn't a complete solution, but helps for common cases. */
|
||||
prop_exist = IDP_GetPropertyFromGroup(group, prop->name);
|
||||
|
||||
if (prop_exist && prop_exist->ui_data) {
|
||||
/* Take ownership of the existing property's UI data. */
|
||||
const eIDPropertyUIDataType src_type = IDP_ui_data_type(prop_exist);
|
||||
IDPropertyUIData *ui_data = prop_exist->ui_data;
|
||||
prop_exist->ui_data = nullptr;
|
||||
|
||||
prop->ui_data = IDP_TryConvertUIData(ui_data, src_type, IDP_ui_data_type(prop));
|
||||
}
|
||||
|
||||
if ((prop_exist != nullptr) && (prop_exist->type == prop->type) &&
|
||||
(prop_exist->subtype == prop->subtype))
|
||||
{
|
||||
/* Preserve prev/next links!!! See #42593. */
|
||||
prop->prev = prop_exist->prev;
|
||||
prop->next = prop_exist->next;
|
||||
prop->flag = prop_exist->flag;
|
||||
|
||||
IDP_FreePropertyContent(prop_exist);
|
||||
*prop_exist = *prop;
|
||||
MEM_freeN(prop);
|
||||
}
|
||||
else {
|
||||
const bool overridable = prop_exist ?
|
||||
(prop_exist->flag & IDP_FLAG_OVERRIDABLE_LIBRARY) != 0 :
|
||||
false;
|
||||
IDP_ReplaceInGroup_ex(group, prop, prop_exist);
|
||||
if (overridable) {
|
||||
prop->flag |= IDP_FLAG_OVERRIDABLE_LIBRARY;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
IDProperty *prop_exist = IDP_GetPropertyFromGroup(group, name);
|
||||
|
||||
/* If existing property is flagged to be statically typed, do not re-type it. Assign the value if
|
||||
* possible (potentially converting it), or fail. See #122743. */
|
||||
if (prop_exist && (prop_exist->flag & IDP_FLAG_STATIC_TYPE) != 0) {
|
||||
IDProperty *prop = idp_from_PyObject(prop_exist, name, ob, true, false);
|
||||
BLI_assert(ELEM(prop, prop_exist, nullptr));
|
||||
if (prop != prop_exist) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"Cannot assign a '%.200s' value to the existing '%s' %s IDProperty",
|
||||
Py_TYPE(ob)->tp_name,
|
||||
name,
|
||||
IDP_type_str(prop_exist));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Attempt to assign new value in existing IDProperty, if types (and potentially subtypes) match
|
||||
* exactly. Otherwise, create a new IDProperty. */
|
||||
IDProperty *new_prop = idp_from_PyObject(prop_exist, name, ob, false, true);
|
||||
if (new_prop == nullptr) {
|
||||
return false;
|
||||
}
|
||||
if (new_prop == prop_exist) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Property was created with no existing counterpart, just insert it in the group container. */
|
||||
if (!prop_exist) {
|
||||
IDP_ReplaceInGroup_ex(group, new_prop, nullptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Try to preserve UI data from the existing, replaced property. See: #37073. */
|
||||
if (prop_exist->ui_data) {
|
||||
/* Take ownership of the existing property's UI data. */
|
||||
const eIDPropertyUIDataType src_type = IDP_ui_data_type(prop_exist);
|
||||
IDPropertyUIData *ui_data = prop_exist->ui_data;
|
||||
prop_exist->ui_data = nullptr;
|
||||
|
||||
new_prop->ui_data = IDP_TryConvertUIData(ui_data, src_type, IDP_ui_data_type(new_prop));
|
||||
}
|
||||
/* Copy over the 'overridable' flag from existing property. */
|
||||
new_prop->flag |= (prop_exist->flag & IDP_FLAG_OVERRIDABLE_LIBRARY);
|
||||
|
||||
IDP_ReplaceInGroup_ex(group, new_prop, prop_exist);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user