Use doxy references to function and enums, also correct some names which became out of sync.
8299 lines
215 KiB
C
8299 lines
215 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 <stdlib.h>
|
|
#include <stddef.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "DNA_ID.h"
|
|
#include "DNA_scene_types.h"
|
|
#include "DNA_constraint_types.h"
|
|
#include "DNA_modifier_types.h"
|
|
#include "DNA_windowmanager_types.h"
|
|
|
|
#include "BLI_blenlib.h"
|
|
#include "BLI_utildefines.h"
|
|
#include "BLI_dynstr.h"
|
|
#include "BLI_ghash.h"
|
|
#include "BLI_math.h"
|
|
|
|
#ifdef DEBUG_OVERRIDE_TIMEIT
|
|
# include "PIL_time_utildefines.h"
|
|
#endif
|
|
|
|
#include "BLF_api.h"
|
|
#include "BLT_translation.h"
|
|
|
|
#include "BKE_animsys.h"
|
|
#include "BKE_context.h"
|
|
#include "BKE_idcode.h"
|
|
#include "BKE_idprop.h"
|
|
#include "BKE_fcurve.h"
|
|
#include "BKE_library_override.h"
|
|
#include "BKE_main.h"
|
|
#include "BKE_report.h"
|
|
|
|
#include "DEG_depsgraph.h"
|
|
|
|
#include "RNA_access.h"
|
|
#include "RNA_define.h"
|
|
#include "RNA_enum_types.h"
|
|
|
|
#include "WM_api.h"
|
|
#include "WM_message.h"
|
|
|
|
/* flush updates */
|
|
#include "DNA_object_types.h"
|
|
#include "WM_types.h"
|
|
|
|
#include "rna_internal.h"
|
|
|
|
const PointerRNA PointerRNA_NULL = {{NULL}};
|
|
|
|
/* Init/Exit */
|
|
|
|
void RNA_init(void)
|
|
{
|
|
StructRNA *srna;
|
|
PropertyRNA *prop;
|
|
|
|
BLENDER_RNA.structs_map = BLI_ghash_str_new_ex(__func__, 2048);
|
|
BLENDER_RNA.structs_len = 0;
|
|
|
|
for (srna = BLENDER_RNA.structs.first; srna; srna = srna->cont.next) {
|
|
if (!srna->cont.prophash) {
|
|
srna->cont.prophash = BLI_ghash_str_new("RNA_init gh");
|
|
|
|
for (prop = srna->cont.properties.first; prop; prop = prop->next) {
|
|
if (!(prop->flag_internal & PROP_INTERN_BUILTIN)) {
|
|
BLI_ghash_insert(srna->cont.prophash, (void *)prop->identifier, prop);
|
|
}
|
|
}
|
|
}
|
|
BLI_assert(srna->flag & STRUCT_PUBLIC_NAMESPACE);
|
|
BLI_ghash_insert(BLENDER_RNA.structs_map, (void *)srna->identifier, srna);
|
|
BLENDER_RNA.structs_len += 1;
|
|
}
|
|
}
|
|
|
|
void RNA_exit(void)
|
|
{
|
|
StructRNA *srna;
|
|
|
|
RNA_property_update_cache_free();
|
|
|
|
for (srna = BLENDER_RNA.structs.first; srna; srna = srna->cont.next) {
|
|
if (srna->cont.prophash) {
|
|
BLI_ghash_free(srna->cont.prophash, NULL, NULL);
|
|
srna->cont.prophash = NULL;
|
|
}
|
|
}
|
|
|
|
RNA_free(&BLENDER_RNA);
|
|
}
|
|
|
|
/* Pointer */
|
|
|
|
void RNA_main_pointer_create(struct Main *main, PointerRNA *r_ptr)
|
|
{
|
|
r_ptr->id.data = NULL;
|
|
r_ptr->type = &RNA_BlendData;
|
|
r_ptr->data = main;
|
|
}
|
|
|
|
void RNA_id_pointer_create(ID *id, PointerRNA *r_ptr)
|
|
{
|
|
StructRNA *type, *idtype = NULL;
|
|
|
|
if (id) {
|
|
PointerRNA tmp = {{NULL}};
|
|
tmp.data = id;
|
|
idtype = rna_ID_refine(&tmp);
|
|
|
|
while (idtype->refine) {
|
|
type = idtype->refine(&tmp);
|
|
|
|
if (type == idtype)
|
|
break;
|
|
else
|
|
idtype = type;
|
|
}
|
|
}
|
|
|
|
r_ptr->id.data = id;
|
|
r_ptr->type = idtype;
|
|
r_ptr->data = id;
|
|
}
|
|
|
|
void RNA_pointer_create(ID *id, StructRNA *type, void *data, PointerRNA *r_ptr)
|
|
{
|
|
#if 0 /* UNUSED */
|
|
StructRNA *idtype = NULL;
|
|
|
|
if (id) {
|
|
PointerRNA tmp = {{0}};
|
|
tmp.data = id;
|
|
idtype = rna_ID_refine(&tmp);
|
|
}
|
|
#endif
|
|
|
|
r_ptr->id.data = id;
|
|
r_ptr->type = type;
|
|
r_ptr->data = data;
|
|
|
|
if (data) {
|
|
while (r_ptr->type && r_ptr->type->refine) {
|
|
StructRNA *rtype = r_ptr->type->refine(r_ptr);
|
|
|
|
if (rtype == r_ptr->type)
|
|
break;
|
|
else
|
|
r_ptr->type = rtype;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool RNA_pointer_is_null(const PointerRNA *ptr)
|
|
{
|
|
return !((ptr->data != NULL) && (ptr->id.data != NULL) && (ptr->type != NULL));
|
|
}
|
|
|
|
static void rna_pointer_inherit_id(StructRNA *type, PointerRNA *parent, PointerRNA *ptr)
|
|
{
|
|
if (type && type->flag & STRUCT_ID) {
|
|
ptr->id.data = ptr->data;
|
|
}
|
|
else {
|
|
ptr->id.data = parent->id.data;
|
|
}
|
|
}
|
|
|
|
void RNA_blender_rna_pointer_create(PointerRNA *r_ptr)
|
|
{
|
|
r_ptr->id.data = NULL;
|
|
r_ptr->type = &RNA_BlenderRNA;
|
|
r_ptr->data = &BLENDER_RNA;
|
|
}
|
|
|
|
PointerRNA rna_pointer_inherit_refine(PointerRNA *ptr, StructRNA *type, void *data)
|
|
{
|
|
if (data) {
|
|
PointerRNA result;
|
|
result.data = data;
|
|
result.type = type;
|
|
rna_pointer_inherit_id(type, ptr, &result);
|
|
|
|
while (result.type->refine) {
|
|
type = result.type->refine(&result);
|
|
|
|
if (type == result.type)
|
|
break;
|
|
else
|
|
result.type = type;
|
|
}
|
|
return result;
|
|
}
|
|
else {
|
|
return PointerRNA_NULL;
|
|
}
|
|
}
|
|
|
|
void RNA_pointer_recast(PointerRNA *ptr, PointerRNA *r_ptr)
|
|
{
|
|
#if 0 /* works but this case if covered by more general code below. */
|
|
if (RNA_struct_is_ID(ptr->type)) {
|
|
/* simple case */
|
|
RNA_id_pointer_create(ptr->id.data, r_ptr);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
StructRNA *base;
|
|
PointerRNA t_ptr;
|
|
*r_ptr = *ptr; /* initialize as the same in case cant recast */
|
|
|
|
for (base = ptr->type->base; base; base = base->base) {
|
|
t_ptr = rna_pointer_inherit_refine(ptr, base, ptr->data);
|
|
if (t_ptr.type && t_ptr.type != ptr->type) {
|
|
*r_ptr = t_ptr;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ID Properties */
|
|
|
|
static void rna_idproperty_touch(IDProperty *idprop)
|
|
{
|
|
/* so the property is seen as 'set' by rna */
|
|
idprop->flag &= ~IDP_FLAG_GHOST;
|
|
}
|
|
|
|
static IDProperty *rna_idproperty_ui_container(PropertyRNA *prop)
|
|
{
|
|
IDProperty *idprop;
|
|
|
|
for (idprop = ((IDProperty *)prop)->prev; idprop; idprop = idprop->prev) {
|
|
if (STREQ(RNA_IDP_UI, idprop->name))
|
|
break;
|
|
}
|
|
|
|
if (idprop == NULL) {
|
|
for (idprop = ((IDProperty *)prop)->next; idprop; idprop = idprop->next) {
|
|
if (STREQ(RNA_IDP_UI, idprop->name))
|
|
break;
|
|
}
|
|
}
|
|
|
|
return idprop;
|
|
}
|
|
|
|
/* return a UI local ID prop definition for this prop */
|
|
static IDProperty *rna_idproperty_ui(PropertyRNA *prop)
|
|
{
|
|
IDProperty *idprop = rna_idproperty_ui_container(prop);
|
|
|
|
if (idprop) {
|
|
return IDP_GetPropertyTypeFromGroup(idprop, ((IDProperty *)prop)->name, IDP_GROUP);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* return or create a UI local ID prop definition for this prop */
|
|
static IDProperty *rna_idproperty_ui_ensure(PointerRNA *ptr, PropertyRNA *prop, bool create)
|
|
{
|
|
IDProperty *idprop = rna_idproperty_ui_container(prop);
|
|
IDPropertyTemplate dummy = { 0 };
|
|
|
|
if (idprop == NULL && create) {
|
|
IDProperty *props = RNA_struct_idprops(ptr, false);
|
|
|
|
/* Sanity check: props is the actual container of this property. */
|
|
if (props != NULL && BLI_findindex(&props->data.group, prop) >= 0) {
|
|
idprop = IDP_New(IDP_GROUP, &dummy, RNA_IDP_UI);
|
|
|
|
if (!IDP_AddToGroup(props, idprop)) {
|
|
IDP_FreeProperty(idprop);
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (idprop) {
|
|
const char *name = ((IDProperty *)prop)->name;
|
|
IDProperty *rv = IDP_GetPropertyTypeFromGroup(idprop, name, IDP_GROUP);
|
|
|
|
if (rv == NULL && create) {
|
|
rv = IDP_New(IDP_GROUP, &dummy, name);
|
|
|
|
if (!IDP_AddToGroup(idprop, rv)) {
|
|
IDP_FreeProperty(rv);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static bool rna_idproperty_ui_set_default(PointerRNA *ptr, PropertyRNA *prop, const char type, IDPropertyTemplate *value)
|
|
{
|
|
BLI_assert(ELEM(type, IDP_INT, IDP_DOUBLE));
|
|
|
|
if (prop->magic == RNA_MAGIC) {
|
|
return false;
|
|
}
|
|
|
|
/* attempt to get the local ID values */
|
|
IDProperty *idp_ui = rna_idproperty_ui_ensure(ptr, prop, value != NULL);
|
|
|
|
if (idp_ui == NULL) {
|
|
return (value == NULL);
|
|
}
|
|
|
|
IDProperty *item = IDP_GetPropertyTypeFromGroup(idp_ui, "default", type);
|
|
|
|
if (value == NULL) {
|
|
if (item != NULL) {
|
|
IDP_RemoveFromGroup(idp_ui, item);
|
|
}
|
|
}
|
|
else {
|
|
if (item != NULL) {
|
|
switch (type) {
|
|
case IDP_INT:
|
|
IDP_Int(item) = value->i;
|
|
break;
|
|
case IDP_DOUBLE:
|
|
IDP_Double(item) = value->d;
|
|
break;
|
|
default:
|
|
BLI_assert(false);
|
|
return false;
|
|
}
|
|
}
|
|
else {
|
|
item = IDP_New(type, value, "default");
|
|
|
|
if (!IDP_AddToGroup(idp_ui, item)) {
|
|
IDP_FreeProperty(item);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
IDProperty *RNA_struct_idprops(PointerRNA *ptr, bool create)
|
|
{
|
|
StructRNA *type = ptr->type;
|
|
|
|
if (type && type->idproperties) {
|
|
return type->idproperties(ptr, create);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
bool RNA_struct_idprops_check(StructRNA *srna)
|
|
{
|
|
return (srna && srna->idproperties);
|
|
}
|
|
|
|
static IDProperty *rna_idproperty_find(PointerRNA *ptr, const char *name)
|
|
{
|
|
IDProperty *group = RNA_struct_idprops(ptr, 0);
|
|
|
|
if (group) {
|
|
if (group->type == IDP_GROUP) {
|
|
return IDP_GetPropertyFromGroup(group, name);
|
|
}
|
|
else {
|
|
/* Not sure why that happens sometimes, with nested properties... */
|
|
/* Seems to be actually array prop, name is usually "0"... To be sorted out later. */
|
|
// printf("Got unexpected IDProp container when trying to retrieve %s: %d\n", name, group->type);
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void rna_idproperty_free(PointerRNA *ptr, const char *name)
|
|
{
|
|
IDProperty *group = RNA_struct_idprops(ptr, 0);
|
|
|
|
if (group) {
|
|
IDProperty *idprop = IDP_GetPropertyFromGroup(group, name);
|
|
if (idprop) {
|
|
IDP_FreeFromGroup(group, idprop);
|
|
}
|
|
}
|
|
}
|
|
|
|
static int rna_ensure_property_array_length(PointerRNA *ptr, PropertyRNA *prop)
|
|
{
|
|
if (prop->magic == RNA_MAGIC) {
|
|
int arraylen[RNA_MAX_ARRAY_DIMENSION];
|
|
return (prop->getlength && ptr->data) ? prop->getlength(ptr, arraylen) : prop->totarraylength;
|
|
}
|
|
else {
|
|
IDProperty *idprop = (IDProperty *)prop;
|
|
|
|
if (idprop->type == IDP_ARRAY)
|
|
return idprop->len;
|
|
else
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static bool rna_ensure_property_array_check(PropertyRNA *prop)
|
|
{
|
|
if (prop->magic == RNA_MAGIC) {
|
|
return (prop->getlength || prop->totarraylength);
|
|
}
|
|
else {
|
|
IDProperty *idprop = (IDProperty *)prop;
|
|
|
|
return (idprop->type == IDP_ARRAY);
|
|
}
|
|
}
|
|
|
|
static void rna_ensure_property_multi_array_length(PointerRNA *ptr, PropertyRNA *prop, int length[])
|
|
{
|
|
if (prop->magic == RNA_MAGIC) {
|
|
if (prop->getlength)
|
|
prop->getlength(ptr, length);
|
|
else
|
|
memcpy(length, prop->arraylength, prop->arraydimension * sizeof(int));
|
|
}
|
|
else {
|
|
IDProperty *idprop = (IDProperty *)prop;
|
|
|
|
if (idprop->type == IDP_ARRAY)
|
|
length[0] = idprop->len;
|
|
else
|
|
length[0] = 0;
|
|
}
|
|
}
|
|
|
|
static bool rna_idproperty_verify_valid(PointerRNA *ptr, PropertyRNA *prop, IDProperty *idprop)
|
|
{
|
|
/* this verifies if the idproperty actually matches the property
|
|
* description and otherwise removes it. this is to ensure that
|
|
* rna property access is type safe, e.g. if you defined the rna
|
|
* to have a certain array length you can count on that staying so */
|
|
|
|
switch (idprop->type) {
|
|
case IDP_IDPARRAY:
|
|
if (prop->type != PROP_COLLECTION)
|
|
return false;
|
|
break;
|
|
case IDP_ARRAY:
|
|
if (rna_ensure_property_array_length(ptr, prop) != idprop->len)
|
|
return false;
|
|
|
|
if (idprop->subtype == IDP_FLOAT && prop->type != PROP_FLOAT)
|
|
return false;
|
|
if (idprop->subtype == IDP_INT && !ELEM(prop->type, PROP_BOOLEAN, PROP_INT, PROP_ENUM))
|
|
return false;
|
|
|
|
break;
|
|
case IDP_INT:
|
|
if (!ELEM(prop->type, PROP_BOOLEAN, PROP_INT, PROP_ENUM))
|
|
return false;
|
|
break;
|
|
case IDP_FLOAT:
|
|
case IDP_DOUBLE:
|
|
if (prop->type != PROP_FLOAT)
|
|
return false;
|
|
break;
|
|
case IDP_STRING:
|
|
if (prop->type != PROP_STRING)
|
|
return false;
|
|
break;
|
|
case IDP_GROUP:
|
|
case IDP_ID:
|
|
if (prop->type != PROP_POINTER)
|
|
return false;
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static PropertyRNA *typemap[IDP_NUMTYPES] = {
|
|
(PropertyRNA *)&rna_PropertyGroupItem_string,
|
|
(PropertyRNA *)&rna_PropertyGroupItem_int,
|
|
(PropertyRNA *)&rna_PropertyGroupItem_float,
|
|
NULL, NULL, NULL,
|
|
(PropertyRNA *)&rna_PropertyGroupItem_group,
|
|
(PropertyRNA *)&rna_PropertyGroupItem_id,
|
|
(PropertyRNA *)&rna_PropertyGroupItem_double,
|
|
(PropertyRNA *)&rna_PropertyGroupItem_idp_array,
|
|
};
|
|
|
|
static PropertyRNA *arraytypemap[IDP_NUMTYPES] = {
|
|
NULL, (PropertyRNA *)&rna_PropertyGroupItem_int_array,
|
|
(PropertyRNA *)&rna_PropertyGroupItem_float_array,
|
|
NULL, NULL, NULL,
|
|
(PropertyRNA *)&rna_PropertyGroupItem_collection, NULL,
|
|
(PropertyRNA *)&rna_PropertyGroupItem_double_array,
|
|
};
|
|
|
|
static void *rna_idproperty_check_ex(PropertyRNA **prop, PointerRNA *ptr, const bool return_rnaprop)
|
|
{
|
|
/* This is quite a hack, but avoids some complexity in the API. we
|
|
* pass IDProperty structs as PropertyRNA pointers to the outside.
|
|
* We store some bytes in PropertyRNA structs that allows us to
|
|
* distinguish it from IDProperty structs. If it is an ID property,
|
|
* we look up an IDP PropertyRNA based on the type, and set the data
|
|
* pointer to the IDProperty. */
|
|
|
|
if ((*prop)->magic == RNA_MAGIC) {
|
|
if ((*prop)->flag & PROP_IDPROPERTY) {
|
|
IDProperty *idprop = rna_idproperty_find(ptr, (*prop)->identifier);
|
|
|
|
if (idprop && !rna_idproperty_verify_valid(ptr, *prop, idprop)) {
|
|
IDProperty *group = RNA_struct_idprops(ptr, 0);
|
|
|
|
IDP_FreeFromGroup(group, idprop);
|
|
return NULL;
|
|
}
|
|
|
|
return idprop;
|
|
}
|
|
else {
|
|
return return_rnaprop ? *prop : NULL;
|
|
}
|
|
}
|
|
|
|
{
|
|
IDProperty *idprop = (IDProperty *)(*prop);
|
|
|
|
if (idprop->type == IDP_ARRAY)
|
|
*prop = arraytypemap[(int)(idprop->subtype)];
|
|
else
|
|
*prop = typemap[(int)(idprop->type)];
|
|
|
|
return idprop;
|
|
}
|
|
}
|
|
|
|
/* This function only returns an IDProperty,
|
|
* or NULL (in case IDProp could not be found, or prop is a real RNA property). */
|
|
IDProperty *rna_idproperty_check(PropertyRNA **prop, PointerRNA *ptr)
|
|
{
|
|
return rna_idproperty_check_ex(prop, ptr, false);
|
|
}
|
|
|
|
/* This function always return the valid, real data pointer, be it a regular RNA property one, or an IDProperty one. */
|
|
PropertyRNA *rna_ensure_property_realdata(PropertyRNA **prop, PointerRNA *ptr)
|
|
{
|
|
return rna_idproperty_check_ex(prop, ptr, true);
|
|
}
|
|
|
|
static PropertyRNA *rna_ensure_property(PropertyRNA *prop)
|
|
{
|
|
/* the quick version if we don't need the idproperty */
|
|
|
|
if (prop->magic == RNA_MAGIC)
|
|
return prop;
|
|
|
|
{
|
|
IDProperty *idprop = (IDProperty *)prop;
|
|
|
|
if (idprop->type == IDP_ARRAY)
|
|
return arraytypemap[(int)(idprop->subtype)];
|
|
else
|
|
return typemap[(int)(idprop->type)];
|
|
}
|
|
}
|
|
|
|
static const char *rna_ensure_property_identifier(const PropertyRNA *prop)
|
|
{
|
|
if (prop->magic == RNA_MAGIC)
|
|
return prop->identifier;
|
|
else
|
|
return ((const IDProperty *)prop)->name;
|
|
}
|
|
|
|
static const char *rna_ensure_property_description(PropertyRNA *prop)
|
|
{
|
|
const char *description = NULL;
|
|
|
|
if (prop->magic == RNA_MAGIC)
|
|
description = prop->description;
|
|
else {
|
|
/* attempt to get the local ID values */
|
|
IDProperty *idp_ui = rna_idproperty_ui(prop);
|
|
|
|
if (idp_ui) {
|
|
IDProperty *item = IDP_GetPropertyTypeFromGroup(idp_ui, "description", IDP_STRING);
|
|
if (item)
|
|
description = IDP_String(item);
|
|
}
|
|
|
|
if (description == NULL)
|
|
description = ((IDProperty *)prop)->name; /* XXX - not correct */
|
|
}
|
|
|
|
return description;
|
|
}
|
|
|
|
static const char *rna_ensure_property_name(const PropertyRNA *prop)
|
|
{
|
|
const char *name;
|
|
|
|
if (prop->magic == RNA_MAGIC)
|
|
name = prop->name;
|
|
else
|
|
name = ((const IDProperty *)prop)->name;
|
|
|
|
return name;
|
|
}
|
|
|
|
/* Structs */
|
|
|
|
StructRNA *RNA_struct_find(const char *identifier)
|
|
{
|
|
return BLI_ghash_lookup(BLENDER_RNA.structs_map, identifier);
|
|
}
|
|
|
|
const char *RNA_struct_identifier(const StructRNA *type)
|
|
{
|
|
return type->identifier;
|
|
}
|
|
|
|
const char *RNA_struct_ui_name(const StructRNA *type)
|
|
{
|
|
return CTX_IFACE_(type->translation_context, type->name);
|
|
}
|
|
|
|
const char *RNA_struct_ui_name_raw(const StructRNA *type)
|
|
{
|
|
return type->name;
|
|
}
|
|
|
|
int RNA_struct_ui_icon(const StructRNA *type)
|
|
{
|
|
if (type)
|
|
return type->icon;
|
|
else
|
|
return ICON_DOT;
|
|
}
|
|
|
|
const char *RNA_struct_ui_description(const StructRNA *type)
|
|
{
|
|
return TIP_(type->description);
|
|
}
|
|
|
|
const char *RNA_struct_ui_description_raw(const StructRNA *type)
|
|
{
|
|
return type->description;
|
|
}
|
|
|
|
const char *RNA_struct_translation_context(const StructRNA *type)
|
|
{
|
|
return type->translation_context;
|
|
}
|
|
|
|
PropertyRNA *RNA_struct_name_property(const StructRNA *type)
|
|
{
|
|
return type->nameproperty;
|
|
}
|
|
|
|
const EnumPropertyItem *RNA_struct_property_tag_defines(const StructRNA *type)
|
|
{
|
|
return type->prop_tag_defines;
|
|
}
|
|
|
|
PropertyRNA *RNA_struct_iterator_property(StructRNA *type)
|
|
{
|
|
return type->iteratorproperty;
|
|
}
|
|
|
|
StructRNA *RNA_struct_base(StructRNA *type)
|
|
{
|
|
return type->base;
|
|
}
|
|
|
|
/**
|
|
* Use to find the subtype directly below a base-type.
|
|
*
|
|
* So if type were `RNA_SpotLIght`, `RNA_struct_base_of(type, &RNA_ID)` would return `&RNA_Light`.
|
|
*/
|
|
const StructRNA *RNA_struct_base_child_of(const StructRNA *type, const StructRNA *parent_type)
|
|
{
|
|
while (type) {
|
|
if (type->base == parent_type) {
|
|
return type;
|
|
}
|
|
type = type->base;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
bool RNA_struct_is_ID(const StructRNA *type)
|
|
{
|
|
return (type->flag & STRUCT_ID) != 0;
|
|
}
|
|
|
|
bool RNA_struct_undo_check(const StructRNA *type)
|
|
{
|
|
return (type->flag & STRUCT_UNDO) != 0;
|
|
}
|
|
|
|
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)
|
|
{
|
|
IDProperty *group = RNA_struct_idprops(ptr, 0);
|
|
|
|
if (group) {
|
|
IDProperty *idp = IDP_GetPropertyFromGroup(group, identifier);
|
|
if (idp) {
|
|
IDP_FreeFromGroup(group, idp);
|
|
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool RNA_struct_is_a(const StructRNA *type, const StructRNA *srna)
|
|
{
|
|
const StructRNA *base;
|
|
|
|
if (srna == &RNA_AnyType)
|
|
return true;
|
|
|
|
if (!type)
|
|
return false;
|
|
|
|
/* ptr->type is always maximally refined */
|
|
for (base = type; base; base = base->base)
|
|
if (base == srna)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
PropertyRNA *RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
|
|
{
|
|
if (identifier[0] == '[' && identifier[1] == '"') { /* " (dummy comment to avoid confusing some
|
|
* function lists in text editors) */
|
|
/* id prop lookup, not so common */
|
|
PropertyRNA *r_prop = NULL;
|
|
PointerRNA r_ptr; /* only support single level props */
|
|
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 */
|
|
PropertyRNA *iterprop = RNA_struct_iterator_property(ptr->type);
|
|
PointerRNA propptr;
|
|
|
|
if (RNA_property_collection_lookup_string(ptr, iterprop, identifier, &propptr))
|
|
return propptr.data;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* Find the property which uses the given nested struct */
|
|
static PropertyRNA *RNA_struct_find_nested(PointerRNA *ptr, StructRNA *srna)
|
|
{
|
|
PropertyRNA *prop = NULL;
|
|
|
|
RNA_STRUCT_BEGIN (ptr, iprop)
|
|
{
|
|
/* This assumes that there can only be one user of this nested struct */
|
|
if (RNA_property_pointer_type(ptr, iprop) == srna) {
|
|
prop = iprop;
|
|
break;
|
|
}
|
|
}
|
|
RNA_PROP_END;
|
|
|
|
return prop;
|
|
}
|
|
|
|
bool RNA_struct_contains_property(PointerRNA *ptr, PropertyRNA *prop_test)
|
|
{
|
|
/* note, prop_test could be freed memory, only use for comparison */
|
|
|
|
/* validate the RNA is ok */
|
|
PropertyRNA *iterprop;
|
|
bool found = false;
|
|
|
|
iterprop = RNA_struct_iterator_property(ptr->type);
|
|
|
|
RNA_PROP_BEGIN (ptr, itemptr, iterprop)
|
|
{
|
|
/* PropertyRNA *prop = itemptr.data; */
|
|
if (prop_test == (PropertyRNA *)itemptr.data) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
RNA_PROP_END;
|
|
|
|
return found;
|
|
}
|
|
|
|
unsigned int RNA_struct_count_properties(StructRNA *srna)
|
|
{
|
|
PointerRNA struct_ptr;
|
|
unsigned int counter = 0;
|
|
|
|
RNA_pointer_create(NULL, srna, NULL, &struct_ptr);
|
|
|
|
RNA_STRUCT_BEGIN (&struct_ptr, prop)
|
|
{
|
|
counter++;
|
|
UNUSED_VARS(prop);
|
|
}
|
|
RNA_STRUCT_END;
|
|
|
|
return counter;
|
|
}
|
|
|
|
/* low level direct access to type->properties, note this ignores parent classes so should be used with care */
|
|
const struct ListBase *RNA_struct_type_properties(StructRNA *srna)
|
|
{
|
|
return &srna->cont.properties;
|
|
}
|
|
|
|
PropertyRNA *RNA_struct_type_find_property(StructRNA *srna, const char *identifier)
|
|
{
|
|
return BLI_findstring_ptr(&srna->cont.properties, identifier, offsetof(PropertyRNA, identifier));
|
|
}
|
|
|
|
FunctionRNA *RNA_struct_find_function(StructRNA *srna, const char *identifier)
|
|
{
|
|
#if 1
|
|
FunctionRNA *func;
|
|
StructRNA *type;
|
|
for (type = srna; type; type = type->base) {
|
|
func = (FunctionRNA *)BLI_findstring_ptr(&type->functions, identifier, offsetof(FunctionRNA, identifier));
|
|
if (func) {
|
|
return func;
|
|
}
|
|
}
|
|
return NULL;
|
|
|
|
/* functional but slow */
|
|
#else
|
|
PointerRNA tptr;
|
|
PropertyRNA *iterprop;
|
|
FunctionRNA *func;
|
|
|
|
RNA_pointer_create(NULL, &RNA_Struct, srna, &tptr);
|
|
iterprop = RNA_struct_find_property(&tptr, "functions");
|
|
|
|
func = NULL;
|
|
|
|
RNA_PROP_BEGIN (&tptr, funcptr, iterprop)
|
|
{
|
|
if (STREQ(identifier, RNA_function_identifier(funcptr.data))) {
|
|
func = funcptr.data;
|
|
break;
|
|
}
|
|
}
|
|
RNA_PROP_END;
|
|
|
|
return func;
|
|
#endif
|
|
}
|
|
|
|
const ListBase *RNA_struct_type_functions(StructRNA *srna)
|
|
{
|
|
return &srna->functions;
|
|
}
|
|
|
|
StructRegisterFunc RNA_struct_register(StructRNA *type)
|
|
{
|
|
return type->reg;
|
|
}
|
|
|
|
StructUnregisterFunc RNA_struct_unregister(StructRNA *type)
|
|
{
|
|
do {
|
|
if (type->unreg)
|
|
return type->unreg;
|
|
} while ((type = type->base));
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void **RNA_struct_instance(PointerRNA *ptr)
|
|
{
|
|
StructRNA *type = ptr->type;
|
|
|
|
do {
|
|
if (type->instance)
|
|
return type->instance(ptr);
|
|
} while ((type = type->base));
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void *RNA_struct_py_type_get(StructRNA *srna)
|
|
{
|
|
return srna->py_type;
|
|
}
|
|
|
|
void RNA_struct_py_type_set(StructRNA *srna, void *py_type)
|
|
{
|
|
srna->py_type = py_type;
|
|
}
|
|
|
|
void *RNA_struct_blender_type_get(StructRNA *srna)
|
|
{
|
|
return srna->blender_type;
|
|
}
|
|
|
|
void RNA_struct_blender_type_set(StructRNA *srna, void *blender_type)
|
|
{
|
|
srna->blender_type = blender_type;
|
|
}
|
|
|
|
char *RNA_struct_name_get_alloc(PointerRNA *ptr, char *fixedbuf, int fixedlen, int *r_len)
|
|
{
|
|
PropertyRNA *nameprop;
|
|
|
|
if (ptr->data && (nameprop = RNA_struct_name_property(ptr->type)))
|
|
return RNA_property_string_get_alloc(ptr, nameprop, fixedbuf, fixedlen, r_len);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* Use when registering structs with the #STRUCT_PUBLIC_NAMESPACE flag.
|
|
*/
|
|
bool RNA_struct_available_or_report(ReportList *reports, const char *identifier)
|
|
{
|
|
const StructRNA *srna_exists = RNA_struct_find(identifier);
|
|
if (UNLIKELY(srna_exists != NULL)) {
|
|
/* Use comprehensive string construction since this is such a rare occurrence
|
|
* and information here may cut down time troubleshooting. */
|
|
DynStr *dynstr = BLI_dynstr_new();
|
|
BLI_dynstr_appendf(dynstr, "Type identifier '%s' is already in use: '", identifier);
|
|
BLI_dynstr_append(dynstr, srna_exists->identifier);
|
|
int i = 0;
|
|
if (srna_exists->base) {
|
|
for (const StructRNA *base = srna_exists->base; base; base = base->base) {
|
|
BLI_dynstr_append(dynstr, "(");
|
|
BLI_dynstr_append(dynstr, base->identifier);
|
|
i += 1;
|
|
}
|
|
while (i--) {
|
|
BLI_dynstr_append(dynstr, ")");
|
|
}
|
|
}
|
|
BLI_dynstr_append(dynstr, "'.");
|
|
char *result = BLI_dynstr_get_cstring(dynstr);
|
|
BLI_dynstr_free(dynstr);
|
|
BKE_report(reports, RPT_ERROR, result);
|
|
MEM_freeN(result);
|
|
return false;
|
|
}
|
|
else {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
bool RNA_struct_bl_idname_ok_or_report(ReportList *reports, const char *identifier, const char *sep)
|
|
{
|
|
const int len_sep = strlen(sep);
|
|
const int len_id = strlen(identifier);
|
|
const char *p = strstr(identifier, sep);
|
|
/* TODO: make error, for now warning until add-ons update. */
|
|
#if 1
|
|
const int report_level = RPT_WARNING;
|
|
const bool failure = true;
|
|
#else
|
|
const int report_level = RPT_ERROR;
|
|
const bool failure = false;
|
|
#endif
|
|
if (p == NULL || p == identifier || p + len_sep >= identifier + len_id) {
|
|
BKE_reportf(reports, report_level, "'%s' doesn't contain '%s' with prefix & suffix", identifier, sep);
|
|
return failure;
|
|
}
|
|
|
|
const char *c, *start, *end, *last;
|
|
start = identifier;
|
|
end = p;
|
|
last = end - 1;
|
|
for (c = start; c != end; c++) {
|
|
if (((*c >= 'A' && *c <= 'Z') ||
|
|
((c != start) && (*c >= '0' && *c <= '9')) ||
|
|
((c != start) && (c != last) && (*c == '_'))) == 0)
|
|
{
|
|
BKE_reportf(reports, report_level, "'%s' doesn't have upper case alpha-numeric prefix", identifier);
|
|
return failure;
|
|
}
|
|
}
|
|
|
|
start = p + len_sep;
|
|
end = identifier + len_id;
|
|
last = end - 1;
|
|
for (c = start; c != end; c++) {
|
|
if (((*c >= 'A' && *c <= 'Z') ||
|
|
(*c >= 'a' && *c <= 'z') ||
|
|
(*c >= '0' && *c <= '9') ||
|
|
((c != start) && (c != last) && (*c == '_'))) == 0)
|
|
{
|
|
BKE_reportf(reports, report_level, "'%s' doesn't have an alpha-numeric suffix", identifier);
|
|
return failure;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/* Property Information */
|
|
|
|
const char *RNA_property_identifier(const PropertyRNA *prop)
|
|
{
|
|
return rna_ensure_property_identifier(prop);
|
|
}
|
|
|
|
const char *RNA_property_description(PropertyRNA *prop)
|
|
{
|
|
return TIP_(rna_ensure_property_description(prop));
|
|
}
|
|
|
|
PropertyType RNA_property_type(PropertyRNA *prop)
|
|
{
|
|
return rna_ensure_property(prop)->type;
|
|
}
|
|
|
|
PropertySubType RNA_property_subtype(PropertyRNA *prop)
|
|
{
|
|
return rna_ensure_property(prop)->subtype;
|
|
}
|
|
|
|
PropertyUnit RNA_property_unit(PropertyRNA *prop)
|
|
{
|
|
return RNA_SUBTYPE_UNIT(rna_ensure_property(prop)->subtype);
|
|
}
|
|
|
|
int RNA_property_flag(PropertyRNA *prop)
|
|
{
|
|
return rna_ensure_property(prop)->flag;
|
|
}
|
|
|
|
int RNA_property_override_flag(PropertyRNA *prop)
|
|
{
|
|
return rna_ensure_property(prop)->flag_override;
|
|
}
|
|
|
|
/**
|
|
* Get the tags set for \a prop as int bitfield.
|
|
* \note Doesn't perform any validity check on the set bits. #RNA_def_property_tags does this
|
|
* in debug builds (to avoid performance issues in non-debug builds), which should be
|
|
* the only way to set tags. Hence, at this point we assume the tag bitfield to be valid.
|
|
*/
|
|
int RNA_property_tags(PropertyRNA *prop)
|
|
{
|
|
return rna_ensure_property(prop)->tags;
|
|
}
|
|
|
|
bool RNA_property_builtin(PropertyRNA *prop)
|
|
{
|
|
return (rna_ensure_property(prop)->flag_internal & PROP_INTERN_BUILTIN) != 0;
|
|
}
|
|
|
|
void *RNA_property_py_data_get(PropertyRNA *prop)
|
|
{
|
|
return prop->py_data;
|
|
}
|
|
|
|
int RNA_property_array_length(PointerRNA *ptr, PropertyRNA *prop)
|
|
{
|
|
return rna_ensure_property_array_length(ptr, prop);
|
|
}
|
|
|
|
bool RNA_property_array_check(PropertyRNA *prop)
|
|
{
|
|
return rna_ensure_property_array_check(prop);
|
|
}
|
|
|
|
/* used by BPY to make an array from the python object */
|
|
int RNA_property_array_dimension(PointerRNA *ptr, PropertyRNA *prop, int length[])
|
|
{
|
|
PropertyRNA *rprop = rna_ensure_property(prop);
|
|
|
|
if (length)
|
|
rna_ensure_property_multi_array_length(ptr, prop, length);
|
|
|
|
return rprop->arraydimension;
|
|
}
|
|
|
|
/* Return the size of Nth dimension. */
|
|
int RNA_property_multi_array_length(PointerRNA *ptr, PropertyRNA *prop, int dim)
|
|
{
|
|
int len[RNA_MAX_ARRAY_DIMENSION];
|
|
|
|
rna_ensure_property_multi_array_length(ptr, prop, len);
|
|
|
|
return len[dim];
|
|
}
|
|
|
|
char RNA_property_array_item_char(PropertyRNA *prop, int index)
|
|
{
|
|
const char *vectoritem = "XYZW";
|
|
const char *quatitem = "WXYZ";
|
|
const char *coloritem = "RGBA";
|
|
PropertySubType subtype = rna_ensure_property(prop)->subtype;
|
|
|
|
BLI_assert(index >= 0);
|
|
|
|
/* get string to use for array index */
|
|
if ((index < 4) && ELEM(subtype, PROP_QUATERNION, PROP_AXISANGLE)) {
|
|
return quatitem[index];
|
|
}
|
|
else if ((index < 4) && ELEM(subtype, PROP_TRANSLATION, PROP_DIRECTION, PROP_XYZ, PROP_XYZ_LENGTH,
|
|
PROP_EULER, PROP_VELOCITY, PROP_ACCELERATION, PROP_COORDS))
|
|
{
|
|
return vectoritem[index];
|
|
}
|
|
else if ((index < 4) && ELEM(subtype, PROP_COLOR, PROP_COLOR_GAMMA)) {
|
|
return coloritem[index];
|
|
}
|
|
|
|
return '\0';
|
|
}
|
|
|
|
int RNA_property_array_item_index(PropertyRNA *prop, char name)
|
|
{
|
|
PropertySubType subtype = rna_ensure_property(prop)->subtype;
|
|
|
|
/* get index based on string name/alias */
|
|
/* maybe a function to find char index in string would be better than all the switches */
|
|
if (ELEM(subtype, PROP_QUATERNION, PROP_AXISANGLE)) {
|
|
switch (name) {
|
|
case 'w':
|
|
return 0;
|
|
case 'x':
|
|
return 1;
|
|
case 'y':
|
|
return 2;
|
|
case 'z':
|
|
return 3;
|
|
}
|
|
}
|
|
else if (ELEM(subtype, PROP_TRANSLATION, PROP_DIRECTION, PROP_XYZ, PROP_XYZ_LENGTH,
|
|
PROP_EULER, PROP_VELOCITY, PROP_ACCELERATION))
|
|
{
|
|
switch (name) {
|
|
case 'x':
|
|
return 0;
|
|
case 'y':
|
|
return 1;
|
|
case 'z':
|
|
return 2;
|
|
case 'w':
|
|
return 3;
|
|
}
|
|
}
|
|
else if (ELEM(subtype, PROP_COLOR, PROP_COLOR_GAMMA)) {
|
|
switch (name) {
|
|
case 'r':
|
|
return 0;
|
|
case 'g':
|
|
return 1;
|
|
case 'b':
|
|
return 2;
|
|
case 'a':
|
|
return 3;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
void RNA_property_int_range(PointerRNA *ptr, PropertyRNA *prop, int *hardmin, int *hardmax)
|
|
{
|
|
IntPropertyRNA *iprop = (IntPropertyRNA *)rna_ensure_property(prop);
|
|
int softmin, softmax;
|
|
|
|
if (prop->magic != RNA_MAGIC) {
|
|
/* attempt to get the local ID values */
|
|
IDProperty *idp_ui = rna_idproperty_ui(prop);
|
|
|
|
if (idp_ui) {
|
|
IDProperty *item;
|
|
|
|
item = IDP_GetPropertyTypeFromGroup(idp_ui, "min", IDP_INT);
|
|
*hardmin = item ? IDP_Int(item) : INT_MIN;
|
|
|
|
item = IDP_GetPropertyTypeFromGroup(idp_ui, "max", IDP_INT);
|
|
*hardmax = item ? IDP_Int(item) : INT_MAX;
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (iprop->range) {
|
|
*hardmin = INT_MIN;
|
|
*hardmax = INT_MAX;
|
|
|
|
iprop->range(ptr, hardmin, hardmax, &softmin, &softmax);
|
|
}
|
|
else if (iprop->range_ex) {
|
|
*hardmin = INT_MIN;
|
|
*hardmax = INT_MAX;
|
|
|
|
iprop->range_ex(ptr, prop, hardmin, hardmax, &softmin, &softmax);
|
|
}
|
|
else {
|
|
*hardmin = iprop->hardmin;
|
|
*hardmax = iprop->hardmax;
|
|
}
|
|
}
|
|
|
|
void RNA_property_int_ui_range(PointerRNA *ptr, PropertyRNA *prop, int *softmin, int *softmax, int *step)
|
|
{
|
|
IntPropertyRNA *iprop = (IntPropertyRNA *)rna_ensure_property(prop);
|
|
int hardmin, hardmax;
|
|
|
|
if (prop->magic != RNA_MAGIC) {
|
|
/* attempt to get the local ID values */
|
|
IDProperty *idp_ui = rna_idproperty_ui(prop);
|
|
|
|
if (idp_ui) {
|
|
IDProperty *item;
|
|
|
|
item = IDP_GetPropertyTypeFromGroup(idp_ui, "soft_min", IDP_INT);
|
|
*softmin = item ? IDP_Int(item) : INT_MIN;
|
|
|
|
item = IDP_GetPropertyTypeFromGroup(idp_ui, "soft_max", IDP_INT);
|
|
*softmax = item ? IDP_Int(item) : INT_MAX;
|
|
|
|
item = IDP_GetPropertyTypeFromGroup(idp_ui, "step", IDP_INT);
|
|
*step = item ? IDP_Int(item) : 1;
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
*softmin = iprop->softmin;
|
|
*softmax = iprop->softmax;
|
|
|
|
if (iprop->range) {
|
|
hardmin = INT_MIN;
|
|
hardmax = INT_MAX;
|
|
|
|
iprop->range(ptr, &hardmin, &hardmax, softmin, softmax);
|
|
|
|
*softmin = max_ii(*softmin, hardmin);
|
|
*softmax = min_ii(*softmax, hardmax);
|
|
}
|
|
else if (iprop->range_ex) {
|
|
hardmin = INT_MIN;
|
|
hardmax = INT_MAX;
|
|
|
|
iprop->range_ex(ptr, prop, &hardmin, &hardmax, softmin, softmax);
|
|
|
|
*softmin = max_ii(*softmin, hardmin);
|
|
*softmax = min_ii(*softmax, hardmax);
|
|
}
|
|
|
|
*step = iprop->step;
|
|
}
|
|
|
|
void RNA_property_float_range(PointerRNA *ptr, PropertyRNA *prop, float *hardmin, float *hardmax)
|
|
{
|
|
FloatPropertyRNA *fprop = (FloatPropertyRNA *)rna_ensure_property(prop);
|
|
float softmin, softmax;
|
|
|
|
if (prop->magic != RNA_MAGIC) {
|
|
/* attempt to get the local ID values */
|
|
IDProperty *idp_ui = rna_idproperty_ui(prop);
|
|
|
|
if (idp_ui) {
|
|
IDProperty *item;
|
|
|
|
item = IDP_GetPropertyTypeFromGroup(idp_ui, "min", IDP_DOUBLE);
|
|
*hardmin = item ? (float)IDP_Double(item) : -FLT_MAX;
|
|
|
|
item = IDP_GetPropertyTypeFromGroup(idp_ui, "max", IDP_DOUBLE);
|
|
*hardmax = item ? (float)IDP_Double(item) : FLT_MAX;
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (fprop->range) {
|
|
*hardmin = -FLT_MAX;
|
|
*hardmax = FLT_MAX;
|
|
|
|
fprop->range(ptr, hardmin, hardmax, &softmin, &softmax);
|
|
}
|
|
else if (fprop->range_ex) {
|
|
*hardmin = -FLT_MAX;
|
|
*hardmax = FLT_MAX;
|
|
|
|
fprop->range_ex(ptr, prop, hardmin, hardmax, &softmin, &softmax);
|
|
}
|
|
else {
|
|
*hardmin = fprop->hardmin;
|
|
*hardmax = fprop->hardmax;
|
|
}
|
|
}
|
|
|
|
void RNA_property_float_ui_range(PointerRNA *ptr, PropertyRNA *prop, float *softmin, float *softmax,
|
|
float *step, float *precision)
|
|
{
|
|
FloatPropertyRNA *fprop = (FloatPropertyRNA *)rna_ensure_property(prop);
|
|
float hardmin, hardmax;
|
|
|
|
if (prop->magic != RNA_MAGIC) {
|
|
/* attempt to get the local ID values */
|
|
IDProperty *idp_ui = rna_idproperty_ui(prop);
|
|
|
|
if (idp_ui) {
|
|
IDProperty *item;
|
|
|
|
item = IDP_GetPropertyTypeFromGroup(idp_ui, "soft_min", IDP_DOUBLE);
|
|
*softmin = item ? (float)IDP_Double(item) : -FLT_MAX;
|
|
|
|
item = IDP_GetPropertyTypeFromGroup(idp_ui, "soft_max", IDP_DOUBLE);
|
|
*softmax = item ? (float)IDP_Double(item) : FLT_MAX;
|
|
|
|
item = IDP_GetPropertyTypeFromGroup(idp_ui, "step", IDP_DOUBLE);
|
|
*step = item ? (float)IDP_Double(item) : 1.0f;
|
|
|
|
item = IDP_GetPropertyTypeFromGroup(idp_ui, "precision", IDP_DOUBLE);
|
|
*precision = item ? (float)IDP_Double(item) : 3.0f;
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
*softmin = fprop->softmin;
|
|
*softmax = fprop->softmax;
|
|
|
|
if (fprop->range) {
|
|
hardmin = -FLT_MAX;
|
|
hardmax = FLT_MAX;
|
|
|
|
fprop->range(ptr, &hardmin, &hardmax, softmin, softmax);
|
|
|
|
*softmin = max_ff(*softmin, hardmin);
|
|
*softmax = min_ff(*softmax, hardmax);
|
|
}
|
|
else if (fprop->range_ex) {
|
|
hardmin = -FLT_MAX;
|
|
hardmax = FLT_MAX;
|
|
|
|
fprop->range_ex(ptr, prop, &hardmin, &hardmax, softmin, softmax);
|
|
|
|
*softmin = max_ff(*softmin, hardmin);
|
|
*softmax = min_ff(*softmax, hardmax);
|
|
}
|
|
|
|
*step = fprop->step;
|
|
*precision = (float)fprop->precision;
|
|
}
|
|
|
|
int RNA_property_float_clamp(PointerRNA *ptr, PropertyRNA *prop, float *value)
|
|
{
|
|
float min, max;
|
|
|
|
RNA_property_float_range(ptr, prop, &min, &max);
|
|
|
|
if (*value < min) {
|
|
*value = min;
|
|
return -1;
|
|
}
|
|
else if (*value > max) {
|
|
*value = max;
|
|
return 1;
|
|
}
|
|
else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int RNA_property_int_clamp(PointerRNA *ptr, PropertyRNA *prop, int *value)
|
|
{
|
|
int min, max;
|
|
|
|
RNA_property_int_range(ptr, prop, &min, &max);
|
|
|
|
if (*value < min) {
|
|
*value = min;
|
|
return -1;
|
|
}
|
|
else if (*value > max) {
|
|
*value = max;
|
|
return 1;
|
|
}
|
|
else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* this is the max length including \0 terminator.
|
|
* '0' used when their is no maximum */
|
|
int RNA_property_string_maxlength(PropertyRNA *prop)
|
|
{
|
|
StringPropertyRNA *sprop = (StringPropertyRNA *)rna_ensure_property(prop);
|
|
return sprop->maxlength;
|
|
}
|
|
|
|
StructRNA *RNA_property_pointer_type(PointerRNA *ptr, PropertyRNA *prop)
|
|
{
|
|
prop = rna_ensure_property(prop);
|
|
|
|
if (prop->type == PROP_POINTER) {
|
|
PointerPropertyRNA *pprop = (PointerPropertyRNA *)prop;
|
|
|
|
if (pprop->typef)
|
|
return pprop->typef(ptr);
|
|
else if (pprop->type)
|
|
return pprop->type;
|
|
}
|
|
else if (prop->type == PROP_COLLECTION) {
|
|
CollectionPropertyRNA *cprop = (CollectionPropertyRNA *)prop;
|
|
|
|
if (cprop->item_type)
|
|
return cprop->item_type;
|
|
}
|
|
/* ignore other types, RNA_struct_find_nested calls with unchecked props */
|
|
|
|
return &RNA_UnknownType;
|
|
}
|
|
|
|
bool RNA_property_pointer_poll(PointerRNA *ptr, PropertyRNA *prop, PointerRNA *value)
|
|
{
|
|
prop = rna_ensure_property(prop);
|
|
|
|
if (prop->type == PROP_POINTER) {
|
|
PointerPropertyRNA *pprop = (PointerPropertyRNA *)prop;
|
|
|
|
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);
|
|
return 0;
|
|
}
|
|
|
|
void RNA_property_enum_items_ex(
|
|
bContext *C, PointerRNA *ptr, PropertyRNA *prop, const bool use_static,
|
|
const EnumPropertyItem **r_item, int *r_totitem, bool *r_free)
|
|
{
|
|
EnumPropertyRNA *eprop = (EnumPropertyRNA *)rna_ensure_property(prop);
|
|
|
|
*r_free = false;
|
|
|
|
if (!use_static && eprop->itemf && (C != NULL || (prop->flag & PROP_ENUM_NO_CONTEXT))) {
|
|
const EnumPropertyItem *item;
|
|
|
|
if (prop->flag & PROP_ENUM_NO_CONTEXT)
|
|
item = eprop->itemf(NULL, ptr, prop, r_free);
|
|
else
|
|
item = eprop->itemf(C, ptr, prop, r_free);
|
|
|
|
/* any callbacks returning NULL should be fixed */
|
|
BLI_assert(item != NULL);
|
|
|
|
if (r_totitem) {
|
|
int tot;
|
|
for (tot = 0; item[tot].identifier; tot++) {
|
|
/* pass */
|
|
}
|
|
*r_totitem = tot;
|
|
}
|
|
|
|
*r_item = item;
|
|
}
|
|
else {
|
|
*r_item = eprop->item;
|
|
if (r_totitem)
|
|
*r_totitem = eprop->totitem;
|
|
}
|
|
}
|
|
|
|
void RNA_property_enum_items(
|
|
bContext *C, PointerRNA *ptr, PropertyRNA *prop,
|
|
const EnumPropertyItem **r_item, int *r_totitem, bool *r_free)
|
|
{
|
|
RNA_property_enum_items_ex(C, ptr, prop, false, r_item, r_totitem, r_free);
|
|
}
|
|
|
|
#ifdef WITH_INTERNATIONAL
|
|
static void property_enum_translate(
|
|
PropertyRNA *prop, EnumPropertyItem **r_item, int *r_totitem, bool *r_free)
|
|
{
|
|
if (!(prop->flag & PROP_ENUM_NO_TRANSLATE)) {
|
|
int i;
|
|
|
|
/* Note: Only do those tests once, and then use BLT_pgettext. */
|
|
bool do_iface = BLT_translate_iface();
|
|
bool do_tooltip = BLT_translate_tooltips();
|
|
EnumPropertyItem *nitem;
|
|
|
|
if (!(do_iface || do_tooltip))
|
|
return;
|
|
|
|
if (*r_free) {
|
|
nitem = *r_item;
|
|
}
|
|
else {
|
|
const EnumPropertyItem *item = *r_item;
|
|
int tot;
|
|
|
|
if (r_totitem) {
|
|
tot = *r_totitem;
|
|
}
|
|
else {
|
|
/* count */
|
|
for (tot = 0; item[tot].identifier; tot++) {
|
|
/* pass */
|
|
}
|
|
}
|
|
|
|
nitem = MEM_mallocN(sizeof(EnumPropertyItem) * (tot + 1), "enum_items_gettexted");
|
|
memcpy(nitem, item, sizeof(EnumPropertyItem) * (tot + 1));
|
|
|
|
*r_free = true;
|
|
}
|
|
|
|
for (i = 0; nitem[i].identifier; i++) {
|
|
if (nitem[i].name && do_iface) {
|
|
nitem[i].name = BLT_pgettext(prop->translation_context, nitem[i].name);
|
|
}
|
|
if (nitem[i].description && do_tooltip) {
|
|
nitem[i].description = BLT_pgettext(NULL, nitem[i].description);
|
|
}
|
|
}
|
|
|
|
*r_item = nitem;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void RNA_property_enum_items_gettexted(
|
|
bContext *C, PointerRNA *ptr, PropertyRNA *prop,
|
|
const EnumPropertyItem **r_item, int *r_totitem, bool *r_free)
|
|
{
|
|
RNA_property_enum_items(C, ptr, prop, r_item, r_totitem, r_free);
|
|
|
|
#ifdef WITH_INTERNATIONAL
|
|
/* Normally dropping 'const' is _not_ ok, in this case it's only modified if we own the memory
|
|
* so allow the exception (callers are creating new arrays in this case). */
|
|
property_enum_translate(prop, (EnumPropertyItem **)r_item, r_totitem, r_free);
|
|
#endif
|
|
}
|
|
|
|
void RNA_property_enum_items_gettexted_all(
|
|
bContext *C, PointerRNA *ptr, PropertyRNA *prop,
|
|
const EnumPropertyItem **r_item, int *r_totitem, bool *r_free)
|
|
{
|
|
EnumPropertyRNA *eprop = (EnumPropertyRNA *)rna_ensure_property(prop);
|
|
int mem_size = sizeof(EnumPropertyItem) * (eprop->totitem + 1);
|
|
/* first return all items */
|
|
EnumPropertyItem *item_array = MEM_mallocN(mem_size, "enum_gettext_all");
|
|
*r_free = true;
|
|
memcpy(item_array, eprop->item, mem_size);
|
|
|
|
if (r_totitem) {
|
|
*r_totitem = eprop->totitem;
|
|
}
|
|
|
|
if (eprop->itemf && (C != NULL || (prop->flag & PROP_ENUM_NO_CONTEXT))) {
|
|
const EnumPropertyItem *item;
|
|
int i;
|
|
bool free = false;
|
|
|
|
if (prop->flag & PROP_ENUM_NO_CONTEXT)
|
|
item = eprop->itemf(NULL, ptr, prop, &free);
|
|
else
|
|
item = eprop->itemf(C, ptr, prop, &free);
|
|
|
|
/* any callbacks returning NULL should be fixed */
|
|
BLI_assert(item != NULL);
|
|
|
|
for (i = 0; i < eprop->totitem; i++) {
|
|
bool exists = false;
|
|
int i_fixed;
|
|
|
|
/* items that do not exist on list are returned, but have their names/identifiers NULLed out */
|
|
for (i_fixed = 0; item[i_fixed].identifier; i_fixed++) {
|
|
if (STREQ(item[i_fixed].identifier, item_array[i].identifier)) {
|
|
exists = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!exists) {
|
|
item_array[i].name = NULL;
|
|
item_array[i].identifier = "";
|
|
}
|
|
}
|
|
|
|
if (free) {
|
|
MEM_freeN((void *)item);
|
|
}
|
|
}
|
|
|
|
#ifdef WITH_INTERNATIONAL
|
|
property_enum_translate(prop, &item_array, r_totitem, r_free);
|
|
#endif
|
|
*r_item = item_array;
|
|
}
|
|
|
|
bool RNA_property_enum_value(bContext *C, PointerRNA *ptr, PropertyRNA *prop, const char *identifier, int *r_value)
|
|
{
|
|
const EnumPropertyItem *item;
|
|
bool free;
|
|
bool found;
|
|
|
|
RNA_property_enum_items(C, ptr, prop, &item, NULL, &free);
|
|
|
|
if (item) {
|
|
const int i = RNA_enum_from_identifier(item, identifier);
|
|
if (i != -1) {
|
|
*r_value = item[i].value;
|
|
found = true;
|
|
}
|
|
else {
|
|
found = false;
|
|
}
|
|
|
|
if (free) {
|
|
MEM_freeN((void *)item);
|
|
}
|
|
}
|
|
else {
|
|
found = false;
|
|
}
|
|
return found;
|
|
}
|
|
|
|
bool RNA_enum_identifier(const EnumPropertyItem *item, const int value, const char **r_identifier)
|
|
{
|
|
const int i = RNA_enum_from_value(item, value);
|
|
if (i != -1) {
|
|
*r_identifier = item[i].identifier;
|
|
return true;
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
int RNA_enum_bitflag_identifiers(const EnumPropertyItem *item, const int value, const char **r_identifier)
|
|
{
|
|
int index = 0;
|
|
for (; item->identifier; item++) {
|
|
if (item->identifier[0] && item->value & value) {
|
|
r_identifier[index++] = item->identifier;
|
|
}
|
|
}
|
|
r_identifier[index] = NULL;
|
|
return index;
|
|
}
|
|
|
|
bool RNA_enum_name(const EnumPropertyItem *item, const int value, const char **r_name)
|
|
{
|
|
const int i = RNA_enum_from_value(item, value);
|
|
if (i != -1) {
|
|
*r_name = item[i].name;
|
|
return true;
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool RNA_enum_description(const EnumPropertyItem *item, const int value, const char **r_description)
|
|
{
|
|
const int i = RNA_enum_from_value(item, value);
|
|
if (i != -1) {
|
|
*r_description = item[i].description;
|
|
return true;
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
int RNA_enum_from_identifier(const EnumPropertyItem *item, const char *identifier)
|
|
{
|
|
int i = 0;
|
|
for (; item->identifier; item++, i++) {
|
|
if (item->identifier[0] && STREQ(item->identifier, identifier)) {
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* Take care using this with translated enums,
|
|
* prefer #RNA_enum_from_identifier where possible.
|
|
*/
|
|
int RNA_enum_from_name(const EnumPropertyItem *item, const char *name)
|
|
{
|
|
int i = 0;
|
|
for (; item->identifier; item++, i++) {
|
|
if (item->identifier[0] && STREQ(item->name, name)) {
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int RNA_enum_from_value(const EnumPropertyItem *item, const int value)
|
|
{
|
|
int i = 0;
|
|
for (; item->identifier; item++, i++) {
|
|
if (item->identifier[0] && item->value == value) {
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
unsigned int RNA_enum_items_count(const EnumPropertyItem *item)
|
|
{
|
|
unsigned int i = 0;
|
|
|
|
while (item->identifier) {
|
|
item++;
|
|
i++;
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
bool RNA_property_enum_identifier(bContext *C, PointerRNA *ptr, PropertyRNA *prop, const int value,
|
|
const char **identifier)
|
|
{
|
|
const EnumPropertyItem *item = NULL;
|
|
bool free;
|
|
|
|
RNA_property_enum_items(C, ptr, prop, &item, NULL, &free);
|
|
if (item) {
|
|
bool result;
|
|
result = RNA_enum_identifier(item, value, identifier);
|
|
if (free) {
|
|
MEM_freeN((void *)item);
|
|
}
|
|
return result;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool RNA_property_enum_name(bContext *C, PointerRNA *ptr, PropertyRNA *prop, const int value, const char **name)
|
|
{
|
|
const EnumPropertyItem *item = NULL;
|
|
bool free;
|
|
|
|
RNA_property_enum_items(C, ptr, prop, &item, NULL, &free);
|
|
if (item) {
|
|
bool result;
|
|
result = RNA_enum_name(item, value, name);
|
|
if (free) {
|
|
MEM_freeN((void *)item);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool RNA_property_enum_name_gettexted(bContext *C, PointerRNA *ptr, PropertyRNA *prop, const int value, const char **name)
|
|
{
|
|
bool result;
|
|
|
|
result = RNA_property_enum_name(C, ptr, prop, value, name);
|
|
|
|
if (result) {
|
|
if (!(prop->flag & PROP_ENUM_NO_TRANSLATE)) {
|
|
if (BLT_translate_iface()) {
|
|
*name = BLT_pgettext(prop->translation_context, *name);
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
bool RNA_property_enum_item_from_value(
|
|
bContext *C, PointerRNA *ptr, PropertyRNA *prop, const int value,
|
|
EnumPropertyItem *r_item)
|
|
{
|
|
const EnumPropertyItem *item = NULL;
|
|
bool free;
|
|
|
|
RNA_property_enum_items(C, ptr, prop, &item, NULL, &free);
|
|
if (item) {
|
|
const int i = RNA_enum_from_value(item, value);
|
|
bool result;
|
|
|
|
if (i != -1) {
|
|
*r_item = item[i];
|
|
result = true;
|
|
}
|
|
else {
|
|
result = false;
|
|
}
|
|
|
|
if (free) {
|
|
MEM_freeN((void *)item);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool RNA_property_enum_item_from_value_gettexted(
|
|
bContext *C, PointerRNA *ptr, PropertyRNA *prop, const int value,
|
|
EnumPropertyItem *r_item)
|
|
{
|
|
bool result;
|
|
|
|
result = RNA_property_enum_item_from_value(C, ptr, prop, value, r_item);
|
|
|
|
if (!(prop->flag & PROP_ENUM_NO_TRANSLATE)) {
|
|
if (BLT_translate_iface()) {
|
|
r_item->name = BLT_pgettext(prop->translation_context, r_item->name);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
int RNA_property_enum_bitflag_identifiers(bContext *C, PointerRNA *ptr, PropertyRNA *prop, const int value,
|
|
const char **identifier)
|
|
{
|
|
const EnumPropertyItem *item = NULL;
|
|
bool free;
|
|
|
|
RNA_property_enum_items(C, ptr, prop, &item, NULL, &free);
|
|
if (item) {
|
|
int result;
|
|
result = RNA_enum_bitflag_identifiers(item, value, identifier);
|
|
if (free)
|
|
MEM_freeN((void *)item);
|
|
|
|
return result;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
const char *RNA_property_ui_name(PropertyRNA *prop)
|
|
{
|
|
return CTX_IFACE_(prop->translation_context, rna_ensure_property_name(prop));
|
|
}
|
|
|
|
const char *RNA_property_ui_name_raw(PropertyRNA *prop)
|
|
{
|
|
return rna_ensure_property_name(prop);
|
|
}
|
|
|
|
const char *RNA_property_ui_description(PropertyRNA *prop)
|
|
{
|
|
return TIP_(rna_ensure_property_description(prop));
|
|
}
|
|
|
|
const char *RNA_property_ui_description_raw(PropertyRNA *prop)
|
|
{
|
|
return rna_ensure_property_description(prop);
|
|
}
|
|
|
|
const char *RNA_property_translation_context(PropertyRNA *_prop)
|
|
{
|
|
PropertyRNA *prop = rna_ensure_property(_prop);
|
|
return prop->translation_context;
|
|
}
|
|
|
|
int RNA_property_ui_icon(PropertyRNA *prop)
|
|
{
|
|
return rna_ensure_property(prop)->icon;
|
|
}
|
|
|
|
bool RNA_property_editable(PointerRNA *ptr, PropertyRNA *prop)
|
|
{
|
|
ID *id = ptr->id.data;
|
|
int flag;
|
|
const char *dummy_info;
|
|
|
|
prop = rna_ensure_property(prop);
|
|
flag = prop->editable ? prop->editable(ptr, &dummy_info) : prop->flag;
|
|
|
|
return ((flag & PROP_EDITABLE) &&
|
|
(flag & PROP_REGISTER) == 0 &&
|
|
(!id || ((!ID_IS_LINKED(id) || (prop->flag & PROP_LIB_EXCEPTION)) &&
|
|
(!id->override_static || RNA_property_overridable_get(ptr, prop)))));
|
|
}
|
|
|
|
/**
|
|
* Version of #RNA_property_editable that tries to return additional info in \a r_info that can be exposed in UI.
|
|
*/
|
|
bool RNA_property_editable_info(PointerRNA *ptr, PropertyRNA *prop, const char **r_info)
|
|
{
|
|
ID *id = ptr->id.data;
|
|
int flag;
|
|
|
|
prop = rna_ensure_property(prop);
|
|
*r_info = "";
|
|
|
|
/* get flag */
|
|
if (prop->editable) {
|
|
flag = prop->editable(ptr, r_info);
|
|
}
|
|
else {
|
|
flag = prop->flag;
|
|
if ((flag & PROP_EDITABLE) == 0 || (flag & PROP_REGISTER)) {
|
|
*r_info = N_("This property is for internal use only and can't be edited");
|
|
}
|
|
}
|
|
|
|
/* property from linked data-block */
|
|
if (id) {
|
|
if (ID_IS_LINKED(id) && (prop->flag & PROP_LIB_EXCEPTION) == 0) {
|
|
if (!(*r_info)[0]) {
|
|
*r_info = N_("Can't edit this property from a linked data-block");
|
|
}
|
|
return false;
|
|
}
|
|
if (id->override_static != NULL && !RNA_property_overridable_get(ptr, prop)) {
|
|
if (!(*r_info)[0]) {
|
|
*r_info = N_("Can't edit this property from an override data-block");
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return ((flag & PROP_EDITABLE) && (flag & PROP_REGISTER) == 0);
|
|
}
|
|
|
|
bool RNA_property_editable_flag(PointerRNA *ptr, PropertyRNA *prop)
|
|
{
|
|
int flag;
|
|
const char *dummy_info;
|
|
|
|
prop = rna_ensure_property(prop);
|
|
flag = prop->editable ? prop->editable(ptr, &dummy_info) : prop->flag;
|
|
return (flag & PROP_EDITABLE) != 0;
|
|
}
|
|
|
|
/* same as RNA_property_editable(), except this checks individual items in an array */
|
|
bool RNA_property_editable_index(PointerRNA *ptr, PropertyRNA *prop, int index)
|
|
{
|
|
ID *id;
|
|
int flag;
|
|
|
|
BLI_assert(index >= 0);
|
|
|
|
prop = rna_ensure_property(prop);
|
|
|
|
flag = prop->flag;
|
|
|
|
if (prop->editable) {
|
|
const char *dummy_info;
|
|
flag &= prop->editable(ptr, &dummy_info);
|
|
}
|
|
|
|
if (prop->itemeditable)
|
|
flag &= prop->itemeditable(ptr, index);
|
|
|
|
id = ptr->id.data;
|
|
|
|
return (flag & PROP_EDITABLE) && (!id || !ID_IS_LINKED(id) || (prop->flag & PROP_LIB_EXCEPTION));
|
|
}
|
|
|
|
bool RNA_property_animateable(PointerRNA *ptr, PropertyRNA *prop)
|
|
{
|
|
/* check that base ID-block can support animation data */
|
|
if (!id_can_have_animdata(ptr->id.data))
|
|
return false;
|
|
|
|
prop = rna_ensure_property(prop);
|
|
|
|
if (!(prop->flag & PROP_ANIMATABLE))
|
|
return false;
|
|
|
|
return (prop->flag & PROP_EDITABLE) != 0;
|
|
}
|
|
|
|
bool RNA_property_animated(PointerRNA *ptr, PropertyRNA *prop)
|
|
{
|
|
int len = 1, index;
|
|
bool driven, special;
|
|
|
|
if (!prop)
|
|
return false;
|
|
|
|
if (RNA_property_array_check(prop))
|
|
len = RNA_property_array_length(ptr, prop);
|
|
|
|
for (index = 0; index < len; index++) {
|
|
if (rna_get_fcurve(ptr, prop, index, NULL, NULL, &driven, &special))
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/** \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 overrideable 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_STATICOVERRIDE_LOCAL) {
|
|
return true;
|
|
}
|
|
}
|
|
else if (RNA_struct_is_a(ptr->type, &RNA_Modifier)) {
|
|
ModifierData *mod = ptr->data;
|
|
if (mod->flag & eModifierFlag_StaticOverride_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_STATIC);
|
|
}
|
|
else {
|
|
/* If this is a real 'pure' IDProp (aka custom property), we want to use the IDProp flag. */
|
|
return !(prop->flag_override & PROPOVERRIDE_NO_COMPARISON) && (((IDProperty *)prop)->flag & IDP_FLAG_OVERRIDABLE_STATIC);
|
|
}
|
|
}
|
|
|
|
/* Should only be used for custom properties */
|
|
bool RNA_property_overridable_static_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_STATIC) :
|
|
(idprop->flag & ~IDP_FLAG_OVERRIDABLE_STATIC);
|
|
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->id.data;
|
|
|
|
if (rna_path == NULL || id == NULL || id->override_static == NULL) {
|
|
return false;
|
|
}
|
|
|
|
return (BKE_override_static_property_find(id->override_static, rna_path) != NULL);
|
|
}
|
|
|
|
bool RNA_property_comparable(PointerRNA *UNUSED(ptr), PropertyRNA *prop)
|
|
{
|
|
prop = rna_ensure_property(prop);
|
|
|
|
return !(prop->flag_override & PROPOVERRIDE_NO_COMPARISON);
|
|
}
|
|
|
|
/* this function is to check if its possible to create a valid path from the ID
|
|
* its slow so don't call in a loop */
|
|
bool RNA_property_path_from_ID_check(PointerRNA *ptr, PropertyRNA *prop)
|
|
{
|
|
char *path = RNA_path_from_ID_to_property(ptr, prop);
|
|
bool ret = false;
|
|
|
|
if (path) {
|
|
PointerRNA id_ptr;
|
|
PointerRNA r_ptr;
|
|
PropertyRNA *r_prop;
|
|
|
|
RNA_id_pointer_create(ptr->id.data, &id_ptr);
|
|
if (RNA_path_resolve(&id_ptr, path, &r_ptr, &r_prop) == true) {
|
|
ret = (prop == r_prop);
|
|
}
|
|
MEM_freeN(path);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static void rna_property_update(bContext *C, Main *bmain, Scene *scene, PointerRNA *ptr, PropertyRNA *prop)
|
|
{
|
|
const bool is_rna = (prop->magic == RNA_MAGIC);
|
|
prop = rna_ensure_property(prop);
|
|
|
|
if (is_rna) {
|
|
if (prop->update) {
|
|
/* ideally no context would be needed for update, but there's some
|
|
* parts of the code that need it still, so we have this exception */
|
|
if (prop->flag & PROP_CONTEXT_UPDATE) {
|
|
if (C) {
|
|
if ((prop->flag & PROP_CONTEXT_PROPERTY_UPDATE) == PROP_CONTEXT_PROPERTY_UPDATE) {
|
|
((ContextPropUpdateFunc)prop->update)(C, ptr, prop);
|
|
}
|
|
else {
|
|
((ContextUpdateFunc)prop->update)(C, ptr);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
prop->update(bmain, scene, ptr);
|
|
}
|
|
|
|
#if 1
|
|
/* TODO(campbell): Should eventually be replaced entirely by message bus (below)
|
|
* for now keep since COW, bugs are hard to track when we have other missing updates. */
|
|
if (prop->noteflag) {
|
|
WM_main_add_notifier(prop->noteflag, ptr->id.data);
|
|
}
|
|
#endif
|
|
|
|
/* if C is NULL, we're updating from animation.
|
|
* avoid slow-down from f-curves by not publishing (for now). */
|
|
if (C != NULL) {
|
|
struct wmMsgBus *mbus = CTX_wm_message_bus(C);
|
|
/* we could add NULL check, for now don't */
|
|
WM_msg_publish_rna(mbus, ptr, prop);
|
|
}
|
|
if (ptr->id.data != NULL && ((prop->flag & PROP_NO_DEG_UPDATE) == 0)) {
|
|
const short id_type = GS(((ID *)ptr->id.data)->name);
|
|
if (ID_TYPE_IS_COW(id_type)) {
|
|
DEG_id_tag_update(ptr->id.data, ID_RECALC_COPY_ON_WRITE);
|
|
}
|
|
}
|
|
/* End message bus. */
|
|
}
|
|
|
|
if (!is_rna || (prop->flag & PROP_IDPROPERTY)) {
|
|
/* WARNING! This is so property drivers update the display!
|
|
* not especially nice */
|
|
DEG_id_tag_update(ptr->id.data, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
|
|
WM_main_add_notifier(NC_WINDOW, NULL);
|
|
/* Not nice as well, but the only way to make sure material preview
|
|
* is updated with custom nodes.
|
|
*/
|
|
if ((prop->flag & PROP_IDPROPERTY) != 0 &&
|
|
(ptr->id.data != NULL) &&
|
|
(GS(((ID *)ptr->id.data)->name) == ID_NT))
|
|
{
|
|
WM_main_add_notifier(NC_MATERIAL | ND_SHADING, NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* must keep in sync with 'rna_property_update'
|
|
* note, its possible this returns a false positive in the case of PROP_CONTEXT_UPDATE
|
|
* but this isn't likely to be a performance problem. */
|
|
bool RNA_property_update_check(PropertyRNA *prop)
|
|
{
|
|
return (prop->magic != RNA_MAGIC || prop->update || prop->noteflag);
|
|
}
|
|
|
|
void RNA_property_update(bContext *C, PointerRNA *ptr, PropertyRNA *prop)
|
|
{
|
|
rna_property_update(C, CTX_data_main(C), CTX_data_scene(C), ptr, prop);
|
|
}
|
|
|
|
void RNA_property_update_main(Main *bmain, Scene *scene, PointerRNA *ptr, PropertyRNA *prop)
|
|
{
|
|
rna_property_update(NULL, bmain, scene, ptr, prop);
|
|
}
|
|
|
|
|
|
/* RNA Updates Cache ------------------------ */
|
|
/* Overview of RNA Update cache system:
|
|
*
|
|
* RNA Update calls need to be cached in order to maintain reasonable performance
|
|
* of the animation system (i.e. maintaining a somewhat interactive framerate)
|
|
* while still allowing updates to be called (necessary in particular for modifier
|
|
* property updates to actually work).
|
|
*
|
|
* The cache is structured with a dual-layer structure
|
|
* - L1 = PointerRNA used as key; id.data is used (it should always be defined,
|
|
* and most updates end up using just that anyways)
|
|
* - L2 = Update functions to be called on those PointerRNA's
|
|
*/
|
|
|
|
/* cache element */
|
|
typedef struct tRnaUpdateCacheElem {
|
|
struct tRnaUpdateCacheElem *next, *prev;
|
|
|
|
PointerRNA ptr; /* L1 key - id as primary, data secondary/ignored? */
|
|
ListBase L2Funcs; /* L2 functions (LinkData<RnaUpdateFuncRef>) */
|
|
} tRnaUpdateCacheElem;
|
|
|
|
/* cache global (tRnaUpdateCacheElem's) - only accessible using these API calls */
|
|
static ListBase rna_updates_cache = {NULL, NULL};
|
|
|
|
/* ........................... */
|
|
|
|
void RNA_property_update_cache_add(PointerRNA *ptr, PropertyRNA *prop)
|
|
{
|
|
const bool is_rna = (prop->magic == RNA_MAGIC);
|
|
tRnaUpdateCacheElem *uce = NULL;
|
|
UpdateFunc fn = NULL;
|
|
LinkData *ld;
|
|
|
|
/* sanity check */
|
|
if (NULL == ptr)
|
|
return;
|
|
|
|
prop = rna_ensure_property(prop);
|
|
|
|
/* we can only handle update calls with no context args for now (makes animsys updates easier) */
|
|
if ((is_rna == false) || (prop->update == NULL) || (prop->flag & PROP_CONTEXT_UPDATE))
|
|
return;
|
|
fn = prop->update;
|
|
|
|
/* find cache element for which key matches... */
|
|
for (uce = rna_updates_cache.first; uce; uce = uce->next) {
|
|
/* just match by id only for now, since most update calls that we'll encounter only really care about this */
|
|
/* TODO: later, the cache might need to have some nesting on L1 to cope better
|
|
* with these problems + some tagging to indicate we need this */
|
|
if (uce->ptr.id.data == ptr->id.data)
|
|
break;
|
|
}
|
|
if (uce == NULL) {
|
|
/* create new instance */
|
|
uce = MEM_callocN(sizeof(tRnaUpdateCacheElem), "tRnaUpdateCacheElem");
|
|
BLI_addtail(&rna_updates_cache, uce);
|
|
|
|
/* copy pointer */
|
|
RNA_pointer_create(ptr->id.data, ptr->type, ptr->data, &uce->ptr);
|
|
}
|
|
|
|
/* check on the update func */
|
|
for (ld = uce->L2Funcs.first; ld; ld = ld->next) {
|
|
/* stop on match - function already cached */
|
|
if (fn == ld->data)
|
|
return;
|
|
}
|
|
/* else... if still here, we need to add it */
|
|
BLI_addtail(&uce->L2Funcs, BLI_genericNodeN(fn));
|
|
}
|
|
|
|
void RNA_property_update_cache_flush(Main *bmain, Scene *scene)
|
|
{
|
|
tRnaUpdateCacheElem *uce;
|
|
|
|
/* TODO: should we check that bmain and scene are valid? The above stuff doesn't! */
|
|
|
|
/* execute the cached updates */
|
|
for (uce = rna_updates_cache.first; uce; uce = uce->next) {
|
|
LinkData *ld;
|
|
|
|
for (ld = uce->L2Funcs.first; ld; ld = ld->next) {
|
|
UpdateFunc fn = (UpdateFunc)ld->data;
|
|
fn(bmain, scene, &uce->ptr);
|
|
}
|
|
}
|
|
}
|
|
|
|
void RNA_property_update_cache_free(void)
|
|
{
|
|
tRnaUpdateCacheElem *uce, *ucn;
|
|
|
|
for (uce = rna_updates_cache.first; uce; uce = ucn) {
|
|
ucn = uce->next;
|
|
|
|
/* free L2 cache */
|
|
BLI_freelistN(&uce->L2Funcs);
|
|
|
|
/* remove self */
|
|
BLI_freelinkN(&rna_updates_cache, uce);
|
|
}
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
/* Property Data */
|
|
|
|
bool RNA_property_boolean_get(PointerRNA *ptr, PropertyRNA *prop)
|
|
{
|
|
BoolPropertyRNA *bprop = (BoolPropertyRNA *)prop;
|
|
IDProperty *idprop;
|
|
bool value;
|
|
|
|
BLI_assert(RNA_property_type(prop) == PROP_BOOLEAN);
|
|
BLI_assert(RNA_property_array_check(prop) == false);
|
|
|
|
if ((idprop = rna_idproperty_check(&prop, ptr)))
|
|
value = IDP_Int(idprop) != 0;
|
|
else if (bprop->get)
|
|
value = bprop->get(ptr);
|
|
else if (bprop->get_ex)
|
|
value = bprop->get_ex(ptr, prop);
|
|
else
|
|
value = bprop->defaultvalue;
|
|
|
|
BLI_assert(ELEM(value, false, true));
|
|
|
|
return value;
|
|
}
|
|
|
|
void RNA_property_boolean_set(PointerRNA *ptr, PropertyRNA *prop, bool value)
|
|
{
|
|
BoolPropertyRNA *bprop = (BoolPropertyRNA *)prop;
|
|
IDProperty *idprop;
|
|
|
|
BLI_assert(RNA_property_type(prop) == PROP_BOOLEAN);
|
|
BLI_assert(RNA_property_array_check(prop) == false);
|
|
BLI_assert(ELEM(value, false, true));
|
|
|
|
/* just in case other values are passed */
|
|
BLI_assert(ELEM(value, true, false));
|
|
|
|
if ((idprop = rna_idproperty_check(&prop, ptr))) {
|
|
IDP_Int(idprop) = (int)value;
|
|
rna_idproperty_touch(idprop);
|
|
}
|
|
else if (bprop->set) {
|
|
bprop->set(ptr, value);
|
|
}
|
|
else if (bprop->set_ex) {
|
|
bprop->set_ex(ptr, prop, value);
|
|
}
|
|
else if (prop->flag & PROP_EDITABLE) {
|
|
IDPropertyTemplate val = {0};
|
|
IDProperty *group;
|
|
|
|
val.i = value;
|
|
|
|
group = RNA_struct_idprops(ptr, 1);
|
|
if (group)
|
|
IDP_AddToGroup(group, IDP_New(IDP_INT, &val, prop->identifier));
|
|
}
|
|
}
|
|
|
|
static void rna_property_boolean_get_default_array_values(BoolPropertyRNA *bprop, bool *values)
|
|
{
|
|
unsigned int length = bprop->property.totarraylength;
|
|
|
|
if (bprop->defaultarray) {
|
|
memcpy(values, bprop->defaultarray, sizeof(bool) * length);
|
|
}
|
|
else {
|
|
for (unsigned int i = 0; i < length; i++) {
|
|
values[i] = bprop->defaultvalue;
|
|
}
|
|
}
|
|
}
|
|
|
|
void RNA_property_boolean_get_array(PointerRNA *ptr, PropertyRNA *prop, bool *values)
|
|
{
|
|
BoolPropertyRNA *bprop = (BoolPropertyRNA *)prop;
|
|
IDProperty *idprop;
|
|
|
|
BLI_assert(RNA_property_type(prop) == PROP_BOOLEAN);
|
|
BLI_assert(RNA_property_array_check(prop) != false);
|
|
|
|
if ((idprop = rna_idproperty_check(&prop, ptr))) {
|
|
if (prop->arraydimension == 0) {
|
|
values[0] = RNA_property_boolean_get(ptr, prop);
|
|
}
|
|
else {
|
|
int *values_src = IDP_Array(idprop);
|
|
for (uint i = 0; i < idprop->len; i++) {
|
|
values[i] = (bool)values_src[i];
|
|
}
|
|
}
|
|
}
|
|
else if (prop->arraydimension == 0)
|
|
values[0] = RNA_property_boolean_get(ptr, prop);
|
|
else if (bprop->getarray)
|
|
bprop->getarray(ptr, values);
|
|
else if (bprop->getarray_ex)
|
|
bprop->getarray_ex(ptr, prop, values);
|
|
else
|
|
rna_property_boolean_get_default_array_values(bprop, values);
|
|
}
|
|
|
|
bool RNA_property_boolean_get_index(PointerRNA *ptr, PropertyRNA *prop, int index)
|
|
{
|
|
bool tmp[RNA_MAX_ARRAY_LENGTH];
|
|
int len = rna_ensure_property_array_length(ptr, prop);
|
|
bool value;
|
|
|
|
BLI_assert(RNA_property_type(prop) == PROP_BOOLEAN);
|
|
BLI_assert(RNA_property_array_check(prop) != false);
|
|
BLI_assert(index >= 0);
|
|
BLI_assert(index < len);
|
|
|
|
if (len <= RNA_MAX_ARRAY_LENGTH) {
|
|
RNA_property_boolean_get_array(ptr, prop, tmp);
|
|
value = tmp[index];
|
|
}
|
|
else {
|
|
bool *tmparray;
|
|
|
|
tmparray = MEM_mallocN(sizeof(bool) * len, __func__);
|
|
RNA_property_boolean_get_array(ptr, prop, tmparray);
|
|
value = tmparray[index];
|
|
MEM_freeN(tmparray);
|
|
}
|
|
|
|
BLI_assert(ELEM(value, false, true));
|
|
|
|
return value;
|
|
}
|
|
|
|
void RNA_property_boolean_set_array(PointerRNA *ptr, PropertyRNA *prop, const bool *values)
|
|
{
|
|
BoolPropertyRNA *bprop = (BoolPropertyRNA *)prop;
|
|
IDProperty *idprop;
|
|
|
|
BLI_assert(RNA_property_type(prop) == PROP_BOOLEAN);
|
|
BLI_assert(RNA_property_array_check(prop) != false);
|
|
|
|
if ((idprop = rna_idproperty_check(&prop, ptr))) {
|
|
if (prop->arraydimension == 0) {
|
|
IDP_Int(idprop) = values[0];
|
|
}
|
|
else {
|
|
int *values_dst = IDP_Array(idprop);
|
|
for (uint i = 0; i < idprop->len; i++) {
|
|
values_dst[i] = (int)values[i];
|
|
}
|
|
}
|
|
rna_idproperty_touch(idprop);
|
|
}
|
|
else if (prop->arraydimension == 0)
|
|
RNA_property_boolean_set(ptr, prop, values[0]);
|
|
else if (bprop->setarray)
|
|
bprop->setarray(ptr, values);
|
|
else if (bprop->setarray_ex)
|
|
bprop->setarray_ex(ptr, prop, values);
|
|
else if (prop->flag & PROP_EDITABLE) {
|
|
IDPropertyTemplate val = {0};
|
|
IDProperty *group;
|
|
|
|
val.array.len = prop->totarraylength;
|
|
val.array.type = IDP_INT;
|
|
|
|
group = RNA_struct_idprops(ptr, 1);
|
|
if (group) {
|
|
idprop = IDP_New(IDP_ARRAY, &val, prop->identifier);
|
|
IDP_AddToGroup(group, idprop);
|
|
int *values_dst = IDP_Array(idprop);
|
|
for (uint i = 0; i < idprop->len; i++) {
|
|
values_dst[i] = (int)values[i];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void RNA_property_boolean_set_index(PointerRNA *ptr, PropertyRNA *prop, int index, bool value)
|
|
{
|
|
bool tmp[RNA_MAX_ARRAY_LENGTH];
|
|
int len = rna_ensure_property_array_length(ptr, prop);
|
|
|
|
BLI_assert(RNA_property_type(prop) == PROP_BOOLEAN);
|
|
BLI_assert(RNA_property_array_check(prop) != false);
|
|
BLI_assert(index >= 0);
|
|
BLI_assert(index < len);
|
|
BLI_assert(ELEM(value, false, true));
|
|
|
|
if (len <= RNA_MAX_ARRAY_LENGTH) {
|
|
RNA_property_boolean_get_array(ptr, prop, tmp);
|
|
tmp[index] = value;
|
|
RNA_property_boolean_set_array(ptr, prop, tmp);
|
|
}
|
|
else {
|
|
bool *tmparray;
|
|
|
|
tmparray = MEM_mallocN(sizeof(bool) * len, __func__);
|
|
RNA_property_boolean_get_array(ptr, prop, tmparray);
|
|
tmparray[index] = value;
|
|
RNA_property_boolean_set_array(ptr, prop, tmparray);
|
|
MEM_freeN(tmparray);
|
|
}
|
|
}
|
|
|
|
bool RNA_property_boolean_get_default(PointerRNA *UNUSED(ptr), PropertyRNA *prop)
|
|
{
|
|
BoolPropertyRNA *bprop = (BoolPropertyRNA *)rna_ensure_property(prop);
|
|
|
|
BLI_assert(RNA_property_type(prop) == PROP_BOOLEAN);
|
|
BLI_assert(RNA_property_array_check(prop) == false);
|
|
BLI_assert(ELEM(bprop->defaultvalue, false, true));
|
|
|
|
return bprop->defaultvalue;
|
|
}
|
|
|
|
void RNA_property_boolean_get_default_array(PointerRNA *UNUSED(ptr), PropertyRNA *prop, bool *values)
|
|
{
|
|
BoolPropertyRNA *bprop = (BoolPropertyRNA *)rna_ensure_property(prop);
|
|
|
|
BLI_assert(RNA_property_type(prop) == PROP_BOOLEAN);
|
|
BLI_assert(RNA_property_array_check(prop) != false);
|
|
|
|
if (prop->arraydimension == 0)
|
|
values[0] = bprop->defaultvalue;
|
|
else
|
|
rna_property_boolean_get_default_array_values(bprop, values);
|
|
}
|
|
|
|
bool RNA_property_boolean_get_default_index(PointerRNA *ptr, PropertyRNA *prop, int index)
|
|
{
|
|
bool tmp[RNA_MAX_ARRAY_LENGTH];
|
|
int len = rna_ensure_property_array_length(ptr, prop);
|
|
|
|
BLI_assert(RNA_property_type(prop) == PROP_BOOLEAN);
|
|
BLI_assert(RNA_property_array_check(prop) != false);
|
|
BLI_assert(index >= 0);
|
|
BLI_assert(index < prop->totarraylength);
|
|
|
|
if (len <= RNA_MAX_ARRAY_LENGTH) {
|
|
RNA_property_boolean_get_default_array(ptr, prop, tmp);
|
|
return tmp[index];
|
|
}
|
|
else {
|
|
bool *tmparray, value;
|
|
|
|
tmparray = MEM_mallocN(sizeof(bool) * len, __func__);
|
|
RNA_property_boolean_get_default_array(ptr, prop, tmparray);
|
|
value = tmparray[index];
|
|
MEM_freeN(tmparray);
|
|
|
|
return value;
|
|
}
|
|
}
|
|
|
|
int RNA_property_int_get(PointerRNA *ptr, PropertyRNA *prop)
|
|
{
|
|
IntPropertyRNA *iprop = (IntPropertyRNA *)prop;
|
|
IDProperty *idprop;
|
|
|
|
BLI_assert(RNA_property_type(prop) == PROP_INT);
|
|
BLI_assert(RNA_property_array_check(prop) == false);
|
|
|
|
if ((idprop = rna_idproperty_check(&prop, ptr)))
|
|
return IDP_Int(idprop);
|
|
else if (iprop->get)
|
|
return iprop->get(ptr);
|
|
else if (iprop->get_ex)
|
|
return iprop->get_ex(ptr, prop);
|
|
else
|
|
return iprop->defaultvalue;
|
|
}
|
|
|
|
void RNA_property_int_set(PointerRNA *ptr, PropertyRNA *prop, int value)
|
|
{
|
|
IntPropertyRNA *iprop = (IntPropertyRNA *)prop;
|
|
IDProperty *idprop;
|
|
|
|
BLI_assert(RNA_property_type(prop) == PROP_INT);
|
|
BLI_assert(RNA_property_array_check(prop) == false);
|
|
/* useful to check on bad values but set function should clamp */
|
|
/* BLI_assert(RNA_property_int_clamp(ptr, prop, &value) == 0); */
|
|
|
|
if ((idprop = rna_idproperty_check(&prop, ptr))) {
|
|
RNA_property_int_clamp(ptr, prop, &value);
|
|
IDP_Int(idprop) = value;
|
|
rna_idproperty_touch(idprop);
|
|
}
|
|
else if (iprop->set)
|
|
iprop->set(ptr, value);
|
|
else if (iprop->set_ex)
|
|
iprop->set_ex(ptr, prop, value);
|
|
else if (prop->flag & PROP_EDITABLE) {
|
|
IDPropertyTemplate val = {0};
|
|
IDProperty *group;
|
|
|
|
RNA_property_int_clamp(ptr, prop, &value);
|
|
|
|
val.i = value;
|
|
|
|
group = RNA_struct_idprops(ptr, 1);
|
|
if (group)
|
|
IDP_AddToGroup(group, IDP_New(IDP_INT, &val, prop->identifier));
|
|
}
|
|
}
|
|
|
|
static void rna_property_int_get_default_array_values(IntPropertyRNA *iprop, int *values)
|
|
{
|
|
unsigned int length = iprop->property.totarraylength;
|
|
|
|
if (iprop->defaultarray) {
|
|
memcpy(values, iprop->defaultarray, sizeof(int) * length);
|
|
}
|
|
else {
|
|
for (unsigned int i = 0; i < length; i++) {
|
|
values[i] = iprop->defaultvalue;
|
|
}
|
|
}
|
|
}
|
|
|
|
void RNA_property_int_get_array(PointerRNA *ptr, PropertyRNA *prop, int *values)
|
|
{
|
|
IntPropertyRNA *iprop = (IntPropertyRNA *)prop;
|
|
IDProperty *idprop;
|
|
|
|
BLI_assert(RNA_property_type(prop) == PROP_INT);
|
|
BLI_assert(RNA_property_array_check(prop) != false);
|
|
|
|
if ((idprop = rna_idproperty_check(&prop, ptr))) {
|
|
BLI_assert(idprop->len == RNA_property_array_length(ptr, prop) || (prop->flag & PROP_IDPROPERTY));
|
|
if (prop->arraydimension == 0)
|
|
values[0] = RNA_property_int_get(ptr, prop);
|
|
else
|
|
memcpy(values, IDP_Array(idprop), sizeof(int) * idprop->len);
|
|
}
|
|
else if (prop->arraydimension == 0)
|
|
values[0] = RNA_property_int_get(ptr, prop);
|
|
else if (iprop->getarray)
|
|
iprop->getarray(ptr, values);
|
|
else if (iprop->getarray_ex)
|
|
iprop->getarray_ex(ptr, prop, values);
|
|
else
|
|
rna_property_int_get_default_array_values(iprop, values);
|
|
}
|
|
|
|
void RNA_property_int_get_array_range(PointerRNA *ptr, PropertyRNA *prop, int values[2])
|
|
{
|
|
const int array_len = RNA_property_array_length(ptr, prop);
|
|
|
|
if (array_len <= 0) {
|
|
values[0] = 0;
|
|
values[1] = 0;
|
|
}
|
|
else if (array_len == 1) {
|
|
RNA_property_int_get_array(ptr, prop, values);
|
|
values[1] = values[0];
|
|
}
|
|
else {
|
|
int arr_stack[32];
|
|
int *arr;
|
|
int i;
|
|
|
|
if (array_len > 32) {
|
|
arr = MEM_mallocN(sizeof(int) * array_len, __func__);
|
|
}
|
|
else {
|
|
arr = arr_stack;
|
|
}
|
|
|
|
RNA_property_int_get_array(ptr, prop, arr);
|
|
values[0] = values[1] = arr[0];
|
|
for (i = 1; i < array_len; i++) {
|
|
values[0] = MIN2(values[0], arr[i]);
|
|
values[1] = MAX2(values[1], arr[i]);
|
|
}
|
|
|
|
if (arr != arr_stack) {
|
|
MEM_freeN(arr);
|
|
}
|
|
}
|
|
}
|
|
|
|
int RNA_property_int_get_index(PointerRNA *ptr, PropertyRNA *prop, int index)
|
|
{
|
|
int tmp[RNA_MAX_ARRAY_LENGTH];
|
|
int len = rna_ensure_property_array_length(ptr, prop);
|
|
|
|
BLI_assert(RNA_property_type(prop) == PROP_INT);
|
|
BLI_assert(RNA_property_array_check(prop) != false);
|
|
BLI_assert(index >= 0);
|
|
BLI_assert(index < len);
|
|
|
|
if (len <= RNA_MAX_ARRAY_LENGTH) {
|
|
RNA_property_int_get_array(ptr, prop, tmp);
|
|
return tmp[index];
|
|
}
|
|
else {
|
|
int *tmparray, value;
|
|
|
|
tmparray = MEM_mallocN(sizeof(int) * len, __func__);
|
|
RNA_property_int_get_array(ptr, prop, tmparray);
|
|
value = tmparray[index];
|
|
MEM_freeN(tmparray);
|
|
|
|
return value;
|
|
}
|
|
}
|
|
|
|
void RNA_property_int_set_array(PointerRNA *ptr, PropertyRNA *prop, const int *values)
|
|
{
|
|
IntPropertyRNA *iprop = (IntPropertyRNA *)prop;
|
|
IDProperty *idprop;
|
|
|
|
BLI_assert(RNA_property_type(prop) == PROP_INT);
|
|
BLI_assert(RNA_property_array_check(prop) != false);
|
|
|
|
if ((idprop = rna_idproperty_check(&prop, ptr))) {
|
|
BLI_assert(idprop->len == RNA_property_array_length(ptr, prop) || (prop->flag & PROP_IDPROPERTY));
|
|
if (prop->arraydimension == 0)
|
|
IDP_Int(idprop) = values[0];
|
|
else
|
|
memcpy(IDP_Array(idprop), values, sizeof(int) * idprop->len);
|
|
|
|
rna_idproperty_touch(idprop);
|
|
}
|
|
else if (prop->arraydimension == 0)
|
|
RNA_property_int_set(ptr, prop, values[0]);
|
|
else if (iprop->setarray)
|
|
iprop->setarray(ptr, values);
|
|
else if (iprop->setarray_ex)
|
|
iprop->setarray_ex(ptr, prop, values);
|
|
else if (prop->flag & PROP_EDITABLE) {
|
|
IDPropertyTemplate val = {0};
|
|
IDProperty *group;
|
|
|
|
/* TODO: RNA_property_int_clamp_array(ptr, prop, &value); */
|
|
|
|
val.array.len = prop->totarraylength;
|
|
val.array.type = IDP_INT;
|
|
|
|
group = RNA_struct_idprops(ptr, 1);
|
|
if (group) {
|
|
idprop = IDP_New(IDP_ARRAY, &val, prop->identifier);
|
|
IDP_AddToGroup(group, idprop);
|
|
memcpy(IDP_Array(idprop), values, sizeof(int) * idprop->len);
|
|
}
|
|
}
|
|
}
|
|
|
|
void RNA_property_int_set_index(PointerRNA *ptr, PropertyRNA *prop, int index, int value)
|
|
{
|
|
int tmp[RNA_MAX_ARRAY_LENGTH];
|
|
int len = rna_ensure_property_array_length(ptr, prop);
|
|
|
|
BLI_assert(RNA_property_type(prop) == PROP_INT);
|
|
BLI_assert(RNA_property_array_check(prop) != false);
|
|
BLI_assert(index >= 0);
|
|
BLI_assert(index < len);
|
|
|
|
if (len <= RNA_MAX_ARRAY_LENGTH) {
|
|
RNA_property_int_get_array(ptr, prop, tmp);
|
|
tmp[index] = value;
|
|
RNA_property_int_set_array(ptr, prop, tmp);
|
|
}
|
|
else {
|
|
int *tmparray;
|
|
|
|
tmparray = MEM_mallocN(sizeof(int) * len, __func__);
|
|
RNA_property_int_get_array(ptr, prop, tmparray);
|
|
tmparray[index] = value;
|
|
RNA_property_int_set_array(ptr, prop, tmparray);
|
|
MEM_freeN(tmparray);
|
|
}
|
|
}
|
|
|
|
int RNA_property_int_get_default(PointerRNA *UNUSED(ptr), PropertyRNA *prop)
|
|
{
|
|
IntPropertyRNA *iprop = (IntPropertyRNA *)rna_ensure_property(prop);
|
|
|
|
if (prop->magic != RNA_MAGIC) {
|
|
/* attempt to get the local ID values */
|
|
IDProperty *idp_ui = rna_idproperty_ui(prop);
|
|
|
|
if (idp_ui) {
|
|
IDProperty *item;
|
|
|
|
item = IDP_GetPropertyTypeFromGroup(idp_ui, "default", IDP_INT);
|
|
return item ? IDP_Int(item) : iprop->defaultvalue;
|
|
}
|
|
}
|
|
|
|
return iprop->defaultvalue;
|
|
}
|
|
|
|
bool RNA_property_int_set_default(PointerRNA *ptr, PropertyRNA *prop, int value)
|
|
{
|
|
if (value != 0) {
|
|
IDPropertyTemplate val = { .i = value, };
|
|
return rna_idproperty_ui_set_default(ptr, prop, IDP_INT, &val);
|
|
}
|
|
else {
|
|
return rna_idproperty_ui_set_default(ptr, prop, IDP_INT, NULL);
|
|
}
|
|
}
|
|
|
|
void RNA_property_int_get_default_array(PointerRNA *UNUSED(ptr), PropertyRNA *prop, int *values)
|
|
{
|
|
IntPropertyRNA *iprop = (IntPropertyRNA *)rna_ensure_property(prop);
|
|
|
|
BLI_assert(RNA_property_type(prop) == PROP_INT);
|
|
BLI_assert(RNA_property_array_check(prop) != false);
|
|
|
|
if (prop->arraydimension == 0)
|
|
values[0] = iprop->defaultvalue;
|
|
else
|
|
rna_property_int_get_default_array_values(iprop, values);
|
|
}
|
|
|
|
int RNA_property_int_get_default_index(PointerRNA *ptr, PropertyRNA *prop, int index)
|
|
{
|
|
int tmp[RNA_MAX_ARRAY_LENGTH];
|
|
int len = rna_ensure_property_array_length(ptr, prop);
|
|
|
|
BLI_assert(RNA_property_type(prop) == PROP_INT);
|
|
BLI_assert(RNA_property_array_check(prop) != false);
|
|
BLI_assert(index >= 0);
|
|
BLI_assert(index < prop->totarraylength);
|
|
|
|
if (len <= RNA_MAX_ARRAY_LENGTH) {
|
|
RNA_property_int_get_default_array(ptr, prop, tmp);
|
|
return tmp[index];
|
|
}
|
|
else {
|
|
int *tmparray, value;
|
|
|
|
tmparray = MEM_mallocN(sizeof(int) * len, __func__);
|
|
RNA_property_int_get_default_array(ptr, prop, tmparray);
|
|
value = tmparray[index];
|
|
MEM_freeN(tmparray);
|
|
|
|
return value;
|
|
}
|
|
}
|
|
|
|
float RNA_property_float_get(PointerRNA *ptr, PropertyRNA *prop)
|
|
{
|
|
FloatPropertyRNA *fprop = (FloatPropertyRNA *)prop;
|
|
IDProperty *idprop;
|
|
|
|
BLI_assert(RNA_property_type(prop) == PROP_FLOAT);
|
|
BLI_assert(RNA_property_array_check(prop) == false);
|
|
|
|
if ((idprop = rna_idproperty_check(&prop, ptr))) {
|
|
if (idprop->type == IDP_FLOAT)
|
|
return IDP_Float(idprop);
|
|
else
|
|
return (float)IDP_Double(idprop);
|
|
}
|
|
else if (fprop->get)
|
|
return fprop->get(ptr);
|
|
else if (fprop->get_ex)
|
|
return fprop->get_ex(ptr, prop);
|
|
else
|
|
return fprop->defaultvalue;
|
|
}
|
|
|
|
void RNA_property_float_set(PointerRNA *ptr, PropertyRNA *prop, float value)
|
|
{
|
|
FloatPropertyRNA *fprop = (FloatPropertyRNA *)prop;
|
|
IDProperty *idprop;
|
|
|
|
BLI_assert(RNA_property_type(prop) == PROP_FLOAT);
|
|
BLI_assert(RNA_property_array_check(prop) == false);
|
|
/* useful to check on bad values but set function should clamp */
|
|
/* BLI_assert(RNA_property_float_clamp(ptr, prop, &value) == 0); */
|
|
|
|
if ((idprop = rna_idproperty_check(&prop, ptr))) {
|
|
RNA_property_float_clamp(ptr, prop, &value);
|
|
if (idprop->type == IDP_FLOAT)
|
|
IDP_Float(idprop) = value;
|
|
else
|
|
IDP_Double(idprop) = value;
|
|
|
|
rna_idproperty_touch(idprop);
|
|
}
|
|
else if (fprop->set) {
|
|
fprop->set(ptr, value);
|
|
}
|
|
else if (fprop->set_ex) {
|
|
fprop->set_ex(ptr, prop, value);
|
|
}
|
|
else if (prop->flag & PROP_EDITABLE) {
|
|
IDPropertyTemplate val = {0};
|
|
IDProperty *group;
|
|
|
|
RNA_property_float_clamp(ptr, prop, &value);
|
|
|
|
val.f = value;
|
|
|
|
group = RNA_struct_idprops(ptr, 1);
|
|
if (group)
|
|
IDP_AddToGroup(group, IDP_New(IDP_FLOAT, &val, prop->identifier));
|
|
}
|
|
}
|
|
|
|
static void rna_property_float_get_default_array_values(FloatPropertyRNA *fprop, float *values)
|
|
{
|
|
unsigned int length = fprop->property.totarraylength;
|
|
|
|
if (fprop->defaultarray) {
|
|
memcpy(values, fprop->defaultarray, sizeof(float) * length);
|
|
}
|
|
else {
|
|
for (unsigned int i = 0; i < length; i++) {
|
|
values[i] = fprop->defaultvalue;
|
|
}
|
|
}
|
|
}
|
|
|
|
void RNA_property_float_get_array(PointerRNA *ptr, PropertyRNA *prop, float *values)
|
|
{
|
|
FloatPropertyRNA *fprop = (FloatPropertyRNA *)prop;
|
|
IDProperty *idprop;
|
|
int i;
|
|
|
|
BLI_assert(RNA_property_type(prop) == PROP_FLOAT);
|
|
BLI_assert(RNA_property_array_check(prop) != false);
|
|
|
|
if ((idprop = rna_idproperty_check(&prop, ptr))) {
|
|
BLI_assert(idprop->len == RNA_property_array_length(ptr, prop) || (prop->flag & PROP_IDPROPERTY));
|
|
if (prop->arraydimension == 0)
|
|
values[0] = RNA_property_float_get(ptr, prop);
|
|
else if (idprop->subtype == IDP_FLOAT) {
|
|
memcpy(values, IDP_Array(idprop), sizeof(float) * idprop->len);
|
|
}
|
|
else {
|
|
for (i = 0; i < idprop->len; i++)
|
|
values[i] = (float)(((double *)IDP_Array(idprop))[i]);
|
|
}
|
|
}
|
|
else if (prop->arraydimension == 0)
|
|
values[0] = RNA_property_float_get(ptr, prop);
|
|
else if (fprop->getarray)
|
|
fprop->getarray(ptr, values);
|
|
else if (fprop->getarray_ex)
|
|
fprop->getarray_ex(ptr, prop, values);
|
|
else
|
|
rna_property_float_get_default_array_values(fprop, values);
|
|
}
|
|
|
|
void RNA_property_float_get_array_range(PointerRNA *ptr, PropertyRNA *prop, float values[2])
|
|
{
|
|
const int array_len = RNA_property_array_length(ptr, prop);
|
|
|
|
if (array_len <= 0) {
|
|
values[0] = 0.0f;
|
|
values[1] = 0.0f;
|
|
}
|
|
else if (array_len == 1) {
|
|
RNA_property_float_get_array(ptr, prop, values);
|
|
values[1] = values[0];
|
|
}
|
|
else {
|
|
float arr_stack[32];
|
|
float *arr;
|
|
int i;
|
|
|
|
if (array_len > 32) {
|
|
arr = MEM_mallocN(sizeof(float) * array_len, __func__);
|
|
}
|
|
else {
|
|
arr = arr_stack;
|
|
}
|
|
|
|
RNA_property_float_get_array(ptr, prop, arr);
|
|
values[0] = values[1] = arr[0];
|
|
for (i = 1; i < array_len; i++) {
|
|
values[0] = MIN2(values[0], arr[i]);
|
|
values[1] = MAX2(values[1], arr[i]);
|
|
}
|
|
|
|
if (arr != arr_stack) {
|
|
MEM_freeN(arr);
|
|
}
|
|
}
|
|
}
|
|
|
|
float RNA_property_float_get_index(PointerRNA *ptr, PropertyRNA *prop, int index)
|
|
{
|
|
float tmp[RNA_MAX_ARRAY_LENGTH];
|
|
int len = rna_ensure_property_array_length(ptr, prop);
|
|
|
|
BLI_assert(RNA_property_type(prop) == PROP_FLOAT);
|
|
BLI_assert(RNA_property_array_check(prop) != false);
|
|
BLI_assert(index >= 0);
|
|
BLI_assert(index < len);
|
|
|
|
if (len <= RNA_MAX_ARRAY_LENGTH) {
|
|
RNA_property_float_get_array(ptr, prop, tmp);
|
|
return tmp[index];
|
|
}
|
|
else {
|
|
float *tmparray, value;
|
|
|
|
tmparray = MEM_mallocN(sizeof(float) * len, __func__);
|
|
RNA_property_float_get_array(ptr, prop, tmparray);
|
|
value = tmparray[index];
|
|
MEM_freeN(tmparray);
|
|
|
|
return value;
|
|
}
|
|
}
|
|
|
|
void RNA_property_float_set_array(PointerRNA *ptr, PropertyRNA *prop, const float *values)
|
|
{
|
|
FloatPropertyRNA *fprop = (FloatPropertyRNA *)prop;
|
|
IDProperty *idprop;
|
|
int i;
|
|
|
|
BLI_assert(RNA_property_type(prop) == PROP_FLOAT);
|
|
BLI_assert(RNA_property_array_check(prop) != false);
|
|
|
|
if ((idprop = rna_idproperty_check(&prop, ptr))) {
|
|
BLI_assert(idprop->len == RNA_property_array_length(ptr, prop) || (prop->flag & PROP_IDPROPERTY));
|
|
if (prop->arraydimension == 0) {
|
|
if (idprop->type == IDP_FLOAT)
|
|
IDP_Float(idprop) = values[0];
|
|
else
|
|
IDP_Double(idprop) = values[0];
|
|
}
|
|
else if (idprop->subtype == IDP_FLOAT) {
|
|
memcpy(IDP_Array(idprop), values, sizeof(float) * idprop->len);
|
|
}
|
|
else {
|
|
for (i = 0; i < idprop->len; i++)
|
|
((double *)IDP_Array(idprop))[i] = values[i];
|
|
}
|
|
|
|
rna_idproperty_touch(idprop);
|
|
}
|
|
else if (prop->arraydimension == 0)
|
|
RNA_property_float_set(ptr, prop, values[0]);
|
|
else if (fprop->setarray) {
|
|
fprop->setarray(ptr, values);
|
|
}
|
|
else if (fprop->setarray_ex) {
|
|
fprop->setarray_ex(ptr, prop, values);
|
|
}
|
|
else if (prop->flag & PROP_EDITABLE) {
|
|
IDPropertyTemplate val = {0};
|
|
IDProperty *group;
|
|
|
|
/* TODO: RNA_property_float_clamp_array(ptr, prop, &value); */
|
|
|
|
val.array.len = prop->totarraylength;
|
|
val.array.type = IDP_FLOAT;
|
|
|
|
group = RNA_struct_idprops(ptr, 1);
|
|
if (group) {
|
|
idprop = IDP_New(IDP_ARRAY, &val, prop->identifier);
|
|
IDP_AddToGroup(group, idprop);
|
|
memcpy(IDP_Array(idprop), values, sizeof(float) * idprop->len);
|
|
}
|
|
}
|
|
}
|
|
|
|
void RNA_property_float_set_index(PointerRNA *ptr, PropertyRNA *prop, int index, float value)
|
|
{
|
|
float tmp[RNA_MAX_ARRAY_LENGTH];
|
|
int len = rna_ensure_property_array_length(ptr, prop);
|
|
|
|
BLI_assert(RNA_property_type(prop) == PROP_FLOAT);
|
|
BLI_assert(RNA_property_array_check(prop) != false);
|
|
BLI_assert(index >= 0);
|
|
BLI_assert(index < len);
|
|
|
|
if (len <= RNA_MAX_ARRAY_LENGTH) {
|
|
RNA_property_float_get_array(ptr, prop, tmp);
|
|
tmp[index] = value;
|
|
RNA_property_float_set_array(ptr, prop, tmp);
|
|
}
|
|
else {
|
|
float *tmparray;
|
|
|
|
tmparray = MEM_mallocN(sizeof(float) * len, __func__);
|
|
RNA_property_float_get_array(ptr, prop, tmparray);
|
|
tmparray[index] = value;
|
|
RNA_property_float_set_array(ptr, prop, tmparray);
|
|
MEM_freeN(tmparray);
|
|
}
|
|
}
|
|
|
|
float RNA_property_float_get_default(PointerRNA *UNUSED(ptr), PropertyRNA *prop)
|
|
{
|
|
FloatPropertyRNA *fprop = (FloatPropertyRNA *)rna_ensure_property(prop);
|
|
|
|
BLI_assert(RNA_property_type(prop) == PROP_FLOAT);
|
|
BLI_assert(RNA_property_array_check(prop) == false);
|
|
|
|
if (prop->magic != RNA_MAGIC) {
|
|
/* attempt to get the local ID values */
|
|
IDProperty *idp_ui = rna_idproperty_ui(prop);
|
|
|
|
if (idp_ui) {
|
|
IDProperty *item;
|
|
|
|
item = IDP_GetPropertyTypeFromGroup(idp_ui, "default", IDP_DOUBLE);
|
|
return item ? IDP_Double(item) : fprop->defaultvalue;
|
|
}
|
|
}
|
|
|
|
return fprop->defaultvalue;
|
|
}
|
|
|
|
bool RNA_property_float_set_default(PointerRNA *ptr, PropertyRNA *prop, float value)
|
|
{
|
|
if (value != 0) {
|
|
IDPropertyTemplate val = { .d = value, };
|
|
return rna_idproperty_ui_set_default(ptr, prop, IDP_DOUBLE, &val);
|
|
}
|
|
else {
|
|
return rna_idproperty_ui_set_default(ptr, prop, IDP_DOUBLE, NULL);
|
|
}
|
|
}
|
|
|
|
void RNA_property_float_get_default_array(PointerRNA *UNUSED(ptr), PropertyRNA *prop, float *values)
|
|
{
|
|
FloatPropertyRNA *fprop = (FloatPropertyRNA *)rna_ensure_property(prop);
|
|
|
|
BLI_assert(RNA_property_type(prop) == PROP_FLOAT);
|
|
BLI_assert(RNA_property_array_check(prop) != false);
|
|
|
|
if (prop->arraydimension == 0)
|
|
values[0] = fprop->defaultvalue;
|
|
else
|
|
rna_property_float_get_default_array_values(fprop, values);
|
|
}
|
|
|
|
float RNA_property_float_get_default_index(PointerRNA *ptr, PropertyRNA *prop, int index)
|
|
{
|
|
float tmp[RNA_MAX_ARRAY_LENGTH];
|
|
int len = rna_ensure_property_array_length(ptr, prop);
|
|
|
|
BLI_assert(RNA_property_type(prop) == PROP_FLOAT);
|
|
BLI_assert(RNA_property_array_check(prop) != false);
|
|
BLI_assert(index >= 0);
|
|
BLI_assert(index < prop->totarraylength);
|
|
|
|
if (len <= RNA_MAX_ARRAY_LENGTH) {
|
|
RNA_property_float_get_default_array(ptr, prop, tmp);
|
|
return tmp[index];
|
|
}
|
|
else {
|
|
float *tmparray, value;
|
|
|
|
tmparray = MEM_mallocN(sizeof(float) * len, __func__);
|
|
RNA_property_float_get_default_array(ptr, prop, tmparray);
|
|
value = tmparray[index];
|
|
MEM_freeN(tmparray);
|
|
|
|
return value;
|
|
}
|
|
}
|
|
|
|
void RNA_property_string_get(PointerRNA *ptr, PropertyRNA *prop, char *value)
|
|
{
|
|
StringPropertyRNA *sprop = (StringPropertyRNA *)prop;
|
|
IDProperty *idprop;
|
|
|
|
BLI_assert(RNA_property_type(prop) == PROP_STRING);
|
|
|
|
if ((idprop = rna_idproperty_check(&prop, ptr))) {
|
|
/* editing bytes is not 100% supported
|
|
* since they can contain NIL chars */
|
|
if (idprop->subtype == IDP_STRING_SUB_BYTE) {
|
|
memcpy(value, IDP_String(idprop), idprop->len);
|
|
value[idprop->len] = '\0';
|
|
}
|
|
else {
|
|
memcpy(value, IDP_String(idprop), idprop->len);
|
|
}
|
|
}
|
|
else if (sprop->get) {
|
|
sprop->get(ptr, value);
|
|
}
|
|
else if (sprop->get_ex) {
|
|
sprop->get_ex(ptr, prop, value);
|
|
}
|
|
else {
|
|
strcpy(value, sprop->defaultvalue);
|
|
}
|
|
}
|
|
|
|
char *RNA_property_string_get_alloc(PointerRNA *ptr, PropertyRNA *prop,
|
|
char *fixedbuf, int fixedlen, int *r_len)
|
|
{
|
|
char *buf;
|
|
int length;
|
|
|
|
BLI_assert(RNA_property_type(prop) == PROP_STRING);
|
|
|
|
length = RNA_property_string_length(ptr, prop);
|
|
|
|
if (length + 1 < fixedlen)
|
|
buf = fixedbuf;
|
|
else
|
|
buf = MEM_mallocN(sizeof(char) * (length + 1), "RNA_string_get_alloc");
|
|
|
|
#ifndef NDEBUG
|
|
/* safety check to ensure the string is actually set */
|
|
buf[length] = 255;
|
|
#endif
|
|
|
|
RNA_property_string_get(ptr, prop, buf);
|
|
|
|
#ifndef NDEBUG
|
|
BLI_assert(buf[length] == '\0');
|
|
#endif
|
|
|
|
if (r_len) {
|
|
*r_len = length;
|
|
}
|
|
|
|
return buf;
|
|
}
|
|
|
|
/* this is the length without \0 terminator */
|
|
int RNA_property_string_length(PointerRNA *ptr, PropertyRNA *prop)
|
|
{
|
|
StringPropertyRNA *sprop = (StringPropertyRNA *)prop;
|
|
IDProperty *idprop;
|
|
|
|
BLI_assert(RNA_property_type(prop) == PROP_STRING);
|
|
|
|
if ((idprop = rna_idproperty_check(&prop, ptr))) {
|
|
if (idprop->subtype == IDP_STRING_SUB_BYTE) {
|
|
return idprop->len;
|
|
}
|
|
else {
|
|
#ifndef NDEBUG
|
|
/* these _must_ stay in sync */
|
|
BLI_assert(strlen(IDP_String(idprop)) == idprop->len - 1);
|
|
#endif
|
|
return idprop->len - 1;
|
|
}
|
|
}
|
|
else if (sprop->length)
|
|
return sprop->length(ptr);
|
|
else if (sprop->length_ex)
|
|
return sprop->length_ex(ptr, prop);
|
|
else
|
|
return strlen(sprop->defaultvalue);
|
|
}
|
|
|
|
void RNA_property_string_set(PointerRNA *ptr, PropertyRNA *prop, const char *value)
|
|
{
|
|
StringPropertyRNA *sprop = (StringPropertyRNA *)prop;
|
|
IDProperty *idprop;
|
|
|
|
BLI_assert(RNA_property_type(prop) == PROP_STRING);
|
|
|
|
if ((idprop = rna_idproperty_check(&prop, ptr))) {
|
|
/* both IDP_STRING_SUB_BYTE / IDP_STRING_SUB_UTF8 */
|
|
IDP_AssignString(idprop, value, RNA_property_string_maxlength(prop) - 1);
|
|
rna_idproperty_touch(idprop);
|
|
}
|
|
else if (sprop->set)
|
|
sprop->set(ptr, value); /* set function needs to clamp its self */
|
|
else if (sprop->set_ex)
|
|
sprop->set_ex(ptr, prop, value); /* set function needs to clamp its self */
|
|
else if (prop->flag & PROP_EDITABLE) {
|
|
IDProperty *group;
|
|
|
|
group = RNA_struct_idprops(ptr, 1);
|
|
if (group)
|
|
IDP_AddToGroup(group, IDP_NewString(value, prop->identifier, RNA_property_string_maxlength(prop)));
|
|
}
|
|
}
|
|
|
|
void RNA_property_string_set_bytes(PointerRNA *ptr, PropertyRNA *prop, const char *value, int len)
|
|
{
|
|
StringPropertyRNA *sprop = (StringPropertyRNA *)prop;
|
|
IDProperty *idprop;
|
|
|
|
BLI_assert(RNA_property_type(prop) == PROP_STRING);
|
|
BLI_assert(RNA_property_subtype(prop) == PROP_BYTESTRING);
|
|
|
|
if ((idprop = rna_idproperty_check(&prop, ptr))) {
|
|
IDP_ResizeArray(idprop, len);
|
|
memcpy(idprop->data.pointer, value, (size_t)len);
|
|
|
|
rna_idproperty_touch(idprop);
|
|
}
|
|
else if (sprop->set) {
|
|
/* XXX, should take length argument (currently not used). */
|
|
sprop->set(ptr, value); /* set function needs to clamp its self */
|
|
}
|
|
else if (sprop->set_ex) {
|
|
/* XXX, should take length argument (currently not used). */
|
|
sprop->set_ex(ptr, prop, value); /* set function needs to clamp its self */
|
|
}
|
|
else if (prop->flag & PROP_EDITABLE) {
|
|
IDProperty *group;
|
|
|
|
group = RNA_struct_idprops(ptr, 1);
|
|
if (group) {
|
|
IDPropertyTemplate val = {0};
|
|
val.string.str = value;
|
|
val.string.len = len;
|
|
val.string.subtype = IDP_STRING_SUB_BYTE;
|
|
IDP_AddToGroup(group, IDP_New(IDP_STRING, &val, prop->identifier));
|
|
}
|
|
}
|
|
}
|
|
|
|
void RNA_property_string_get_default(PointerRNA *UNUSED(ptr), PropertyRNA *prop, char *value)
|
|
{
|
|
StringPropertyRNA *sprop = (StringPropertyRNA *)rna_ensure_property(prop);
|
|
|
|
BLI_assert(RNA_property_type(prop) == PROP_STRING);
|
|
|
|
strcpy(value, sprop->defaultvalue);
|
|
}
|
|
|
|
char *RNA_property_string_get_default_alloc(PointerRNA *ptr, PropertyRNA *prop, char *fixedbuf, int fixedlen)
|
|
{
|
|
char *buf;
|
|
int length;
|
|
|
|
BLI_assert(RNA_property_type(prop) == PROP_STRING);
|
|
|
|
length = RNA_property_string_default_length(ptr, prop);
|
|
|
|
if (length + 1 < fixedlen)
|
|
buf = fixedbuf;
|
|
else
|
|
buf = MEM_callocN(sizeof(char) * (length + 1), "RNA_string_get_alloc");
|
|
|
|
RNA_property_string_get_default(ptr, prop, buf);
|
|
|
|
return buf;
|
|
}
|
|
|
|
/* this is the length without \0 terminator */
|
|
int RNA_property_string_default_length(PointerRNA *UNUSED(ptr), PropertyRNA *prop)
|
|
{
|
|
StringPropertyRNA *sprop = (StringPropertyRNA *)prop;
|
|
|
|
BLI_assert(RNA_property_type(prop) == PROP_STRING);
|
|
|
|
return strlen(sprop->defaultvalue);
|
|
}
|
|
|
|
int RNA_property_enum_get(PointerRNA *ptr, PropertyRNA *prop)
|
|
{
|
|
EnumPropertyRNA *eprop = (EnumPropertyRNA *)prop;
|
|
IDProperty *idprop;
|
|
|
|
BLI_assert(RNA_property_type(prop) == PROP_ENUM);
|
|
|
|
if ((idprop = rna_idproperty_check(&prop, ptr)))
|
|
return IDP_Int(idprop);
|
|
else if (eprop->get)
|
|
return eprop->get(ptr);
|
|
else if (eprop->get_ex)
|
|
return eprop->get_ex(ptr, prop);
|
|
else
|
|
return eprop->defaultvalue;
|
|
}
|
|
|
|
void RNA_property_enum_set(PointerRNA *ptr, PropertyRNA *prop, int value)
|
|
{
|
|
EnumPropertyRNA *eprop = (EnumPropertyRNA *)prop;
|
|
IDProperty *idprop;
|
|
|
|
BLI_assert(RNA_property_type(prop) == PROP_ENUM);
|
|
|
|
if ((idprop = rna_idproperty_check(&prop, ptr))) {
|
|
IDP_Int(idprop) = value;
|
|
rna_idproperty_touch(idprop);
|
|
}
|
|
else if (eprop->set) {
|
|
eprop->set(ptr, value);
|
|
}
|
|
else if (eprop->set_ex) {
|
|
eprop->set_ex(ptr, prop, value);
|
|
}
|
|
else if (prop->flag & PROP_EDITABLE) {
|
|
IDPropertyTemplate val = {0};
|
|
IDProperty *group;
|
|
|
|
val.i = value;
|
|
|
|
group = RNA_struct_idprops(ptr, 1);
|
|
if (group)
|
|
IDP_AddToGroup(group, IDP_New(IDP_INT, &val, prop->identifier));
|
|
}
|
|
}
|
|
|
|
int RNA_property_enum_get_default(PointerRNA *UNUSED(ptr), PropertyRNA *prop)
|
|
{
|
|
EnumPropertyRNA *eprop = (EnumPropertyRNA *)rna_ensure_property(prop);
|
|
|
|
BLI_assert(RNA_property_type(prop) == PROP_ENUM);
|
|
|
|
return eprop->defaultvalue;
|
|
}
|
|
|
|
void *RNA_property_enum_py_data_get(PropertyRNA *prop)
|
|
{
|
|
EnumPropertyRNA *eprop = (EnumPropertyRNA *)prop;
|
|
|
|
BLI_assert(RNA_property_type(prop) == PROP_ENUM);
|
|
|
|
return eprop->py_data;
|
|
}
|
|
|
|
/**
|
|
* Get the value of the item that is \a step items away from \a from_value.
|
|
*
|
|
* \param from_value: Item value to start stepping from.
|
|
* \param step: Absolute value defines step size, sign defines direction.
|
|
* E.g to get the next item, pass 1, for the previous -1.
|
|
*/
|
|
int RNA_property_enum_step(const bContext *C, PointerRNA *ptr, PropertyRNA *prop, int from_value, int step)
|
|
{
|
|
const EnumPropertyItem *item_array;
|
|
int totitem;
|
|
bool free;
|
|
int result_value = from_value;
|
|
int i, i_init;
|
|
int single_step = (step < 0) ? -1 : 1;
|
|
int step_tot = 0;
|
|
|
|
RNA_property_enum_items((bContext *)C, ptr, prop, &item_array, &totitem, &free);
|
|
i = RNA_enum_from_value(item_array, from_value);
|
|
i_init = i;
|
|
|
|
do {
|
|
i = mod_i(i + single_step, totitem);
|
|
if (item_array[i].identifier[0]) {
|
|
step_tot += single_step;
|
|
}
|
|
} while ((i != i_init) && (step_tot != step));
|
|
|
|
if (i != i_init) {
|
|
result_value = item_array[i].value;
|
|
}
|
|
|
|
if (free) {
|
|
MEM_freeN((void *)item_array);
|
|
}
|
|
|
|
return result_value;
|
|
}
|
|
|
|
PointerRNA RNA_property_pointer_get(PointerRNA *ptr, PropertyRNA *prop)
|
|
{
|
|
PointerPropertyRNA *pprop = (PointerPropertyRNA *)prop;
|
|
IDProperty *idprop;
|
|
|
|
BLI_assert(RNA_property_type(prop) == PROP_POINTER);
|
|
|
|
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);
|
|
else
|
|
return rna_pointer_inherit_refine(ptr, pprop->type, idprop);
|
|
}
|
|
else if (pprop->get) {
|
|
return pprop->get(ptr);
|
|
}
|
|
else if (prop->flag & PROP_IDPROPERTY) {
|
|
/* XXX temporary hack to add it automatically, reading should
|
|
* never do any write ops, to ensure thread safety etc .. */
|
|
RNA_property_pointer_add(ptr, prop);
|
|
return RNA_property_pointer_get(ptr, prop);
|
|
}
|
|
else {
|
|
return PointerRNA_NULL;
|
|
}
|
|
}
|
|
|
|
void RNA_property_pointer_set(PointerRNA *ptr, PropertyRNA *prop, PointerRNA ptr_value)
|
|
{
|
|
PointerPropertyRNA *pprop = (PointerPropertyRNA *)prop;
|
|
BLI_assert(RNA_property_type(prop) == PROP_POINTER);
|
|
|
|
/* 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;
|
|
}
|
|
|
|
/* 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));
|
|
}
|
|
}
|
|
}
|
|
|
|
PointerRNA RNA_property_pointer_get_default(PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop))
|
|
{
|
|
/*PointerPropertyRNA *pprop = (PointerPropertyRNA *)prop; */
|
|
|
|
/* BLI_assert(RNA_property_type(prop) == PROP_POINTER); */
|
|
|
|
return PointerRNA_NULL; /* FIXME: there has to be a way... */
|
|
}
|
|
|
|
void RNA_property_pointer_add(PointerRNA *ptr, PropertyRNA *prop)
|
|
{
|
|
/*IDProperty *idprop;*/
|
|
|
|
BLI_assert(RNA_property_type(prop) == PROP_POINTER);
|
|
|
|
if ((/*idprop=*/ rna_idproperty_check(&prop, ptr))) {
|
|
/* already exists */
|
|
}
|
|
else if (prop->flag & PROP_IDPROPERTY) {
|
|
IDPropertyTemplate val = {0};
|
|
IDProperty *group;
|
|
|
|
val.i = 0;
|
|
|
|
group = RNA_struct_idprops(ptr, 1);
|
|
if (group)
|
|
IDP_AddToGroup(group, IDP_New(IDP_GROUP, &val, prop->identifier));
|
|
}
|
|
else
|
|
printf("%s %s.%s: only supported for id properties.\n", __func__, ptr->type->identifier, prop->identifier);
|
|
}
|
|
|
|
void RNA_property_pointer_remove(PointerRNA *ptr, PropertyRNA *prop)
|
|
{
|
|
IDProperty *idprop, *group;
|
|
|
|
BLI_assert(RNA_property_type(prop) == PROP_POINTER);
|
|
|
|
if ((idprop = rna_idproperty_check(&prop, ptr))) {
|
|
group = RNA_struct_idprops(ptr, 0);
|
|
|
|
if (group) {
|
|
IDP_FreeFromGroup(group, idprop);
|
|
}
|
|
}
|
|
else
|
|
printf("%s %s.%s: only supported for id properties.\n", __func__, ptr->type->identifier, prop->identifier);
|
|
}
|
|
|
|
static void rna_property_collection_get_idp(CollectionPropertyIterator *iter)
|
|
{
|
|
CollectionPropertyRNA *cprop = (CollectionPropertyRNA *)iter->prop;
|
|
|
|
iter->ptr.data = rna_iterator_array_get(iter);
|
|
iter->ptr.type = cprop->item_type;
|
|
rna_pointer_inherit_id(cprop->item_type, &iter->parent, &iter->ptr);
|
|
}
|
|
|
|
void RNA_property_collection_begin(PointerRNA *ptr, PropertyRNA *prop, CollectionPropertyIterator *iter)
|
|
{
|
|
IDProperty *idprop;
|
|
|
|
BLI_assert(RNA_property_type(prop) == PROP_COLLECTION);
|
|
|
|
memset(iter, 0, sizeof(*iter));
|
|
|
|
if ((idprop = rna_idproperty_check(&prop, ptr)) || (prop->flag & PROP_IDPROPERTY)) {
|
|
iter->parent = *ptr;
|
|
iter->prop = prop;
|
|
|
|
if (idprop)
|
|
rna_iterator_array_begin(iter, IDP_IDPArray(idprop), sizeof(IDProperty), idprop->len, 0, NULL);
|
|
else
|
|
rna_iterator_array_begin(iter, NULL, sizeof(IDProperty), 0, 0, NULL);
|
|
|
|
if (iter->valid)
|
|
rna_property_collection_get_idp(iter);
|
|
|
|
iter->idprop = 1;
|
|
}
|
|
else {
|
|
CollectionPropertyRNA *cprop = (CollectionPropertyRNA *)prop;
|
|
cprop->begin(iter, ptr);
|
|
}
|
|
}
|
|
|
|
void RNA_property_collection_next(CollectionPropertyIterator *iter)
|
|
{
|
|
CollectionPropertyRNA *cprop = (CollectionPropertyRNA *)rna_ensure_property(iter->prop);
|
|
|
|
if (iter->idprop) {
|
|
rna_iterator_array_next(iter);
|
|
|
|
if (iter->valid)
|
|
rna_property_collection_get_idp(iter);
|
|
}
|
|
else
|
|
cprop->next(iter);
|
|
}
|
|
|
|
void RNA_property_collection_skip(CollectionPropertyIterator *iter, int num)
|
|
{
|
|
CollectionPropertyRNA *cprop = (CollectionPropertyRNA *)rna_ensure_property(iter->prop);
|
|
int i;
|
|
|
|
if (num > 1 && (iter->idprop || (cprop->property.flag_internal & PROP_INTERN_RAW_ARRAY))) {
|
|
/* fast skip for array */
|
|
ArrayIterator *internal = &iter->internal.array;
|
|
|
|
if (!internal->skip) {
|
|
internal->ptr += internal->itemsize * (num - 1);
|
|
iter->valid = (internal->ptr < internal->endptr);
|
|
if (iter->valid)
|
|
RNA_property_collection_next(iter);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* slow iteration otherwise */
|
|
for (i = 0; i < num && iter->valid; i++)
|
|
RNA_property_collection_next(iter);
|
|
}
|
|
|
|
void RNA_property_collection_end(CollectionPropertyIterator *iter)
|
|
{
|
|
CollectionPropertyRNA *cprop = (CollectionPropertyRNA *)rna_ensure_property(iter->prop);
|
|
|
|
if (iter->idprop)
|
|
rna_iterator_array_end(iter);
|
|
else
|
|
cprop->end(iter);
|
|
}
|
|
|
|
int RNA_property_collection_length(PointerRNA *ptr, PropertyRNA *prop)
|
|
{
|
|
CollectionPropertyRNA *cprop = (CollectionPropertyRNA *)prop;
|
|
IDProperty *idprop;
|
|
|
|
BLI_assert(RNA_property_type(prop) == PROP_COLLECTION);
|
|
|
|
if ((idprop = rna_idproperty_check(&prop, ptr))) {
|
|
return idprop->len;
|
|
}
|
|
else if (cprop->length) {
|
|
return cprop->length(ptr);
|
|
}
|
|
else {
|
|
CollectionPropertyIterator iter;
|
|
int length = 0;
|
|
|
|
RNA_property_collection_begin(ptr, prop, &iter);
|
|
for (; iter.valid; RNA_property_collection_next(&iter))
|
|
length++;
|
|
RNA_property_collection_end(&iter);
|
|
|
|
return length;
|
|
}
|
|
}
|
|
|
|
void RNA_property_collection_add(PointerRNA *ptr, PropertyRNA *prop, PointerRNA *r_ptr)
|
|
{
|
|
IDProperty *idprop;
|
|
/* CollectionPropertyRNA *cprop = (CollectionPropertyRNA *)prop; */
|
|
|
|
BLI_assert(RNA_property_type(prop) == PROP_COLLECTION);
|
|
|
|
if ((idprop = rna_idproperty_check(&prop, ptr))) {
|
|
IDPropertyTemplate val = {0};
|
|
IDProperty *item;
|
|
|
|
item = IDP_New(IDP_GROUP, &val, "");
|
|
IDP_AppendArray(idprop, item);
|
|
/* IDP_FreeProperty(item); *//* IDP_AppendArray does a shallow copy (memcpy), only free memory */
|
|
MEM_freeN(item);
|
|
rna_idproperty_touch(idprop);
|
|
}
|
|
else if (prop->flag & PROP_IDPROPERTY) {
|
|
IDProperty *group, *item;
|
|
IDPropertyTemplate val = {0};
|
|
|
|
group = RNA_struct_idprops(ptr, 1);
|
|
if (group) {
|
|
idprop = IDP_NewIDPArray(prop->identifier);
|
|
IDP_AddToGroup(group, idprop);
|
|
|
|
item = IDP_New(IDP_GROUP, &val, "");
|
|
IDP_AppendArray(idprop, item);
|
|
/* IDP_FreeProperty(item); *//* IDP_AppendArray does a shallow copy (memcpy), only free memory */
|
|
MEM_freeN(item);
|
|
}
|
|
}
|
|
|
|
/* py api calls directly */
|
|
#if 0
|
|
else if (cprop->add) {
|
|
if (!(cprop->add->flag & FUNC_USE_CONTEXT)) { /* XXX check for this somewhere else */
|
|
ParameterList params;
|
|
RNA_parameter_list_create(¶ms, ptr, cprop->add);
|
|
RNA_function_call(NULL, NULL, ptr, cprop->add, ¶ms);
|
|
RNA_parameter_list_free(¶ms);
|
|
}
|
|
}
|
|
#if 0
|
|
else {
|
|
printf("%s %s.%s: not implemented for this property.\n", __func__, ptr->type->identifier, prop->identifier);
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
if (r_ptr) {
|
|
if (idprop) {
|
|
CollectionPropertyRNA *cprop = (CollectionPropertyRNA *)prop;
|
|
|
|
r_ptr->data = IDP_GetIndexArray(idprop, idprop->len - 1);
|
|
r_ptr->type = cprop->item_type;
|
|
rna_pointer_inherit_id(NULL, ptr, r_ptr);
|
|
}
|
|
else
|
|
memset(r_ptr, 0, sizeof(*r_ptr));
|
|
}
|
|
}
|
|
|
|
bool RNA_property_collection_remove(PointerRNA *ptr, PropertyRNA *prop, int key)
|
|
{
|
|
IDProperty *idprop;
|
|
/* CollectionPropertyRNA *cprop = (CollectionPropertyRNA *)prop; */
|
|
|
|
BLI_assert(RNA_property_type(prop) == PROP_COLLECTION);
|
|
|
|
if ((idprop = rna_idproperty_check(&prop, ptr))) {
|
|
IDProperty tmp, *array;
|
|
int len;
|
|
|
|
len = idprop->len;
|
|
array = IDP_IDPArray(idprop);
|
|
|
|
if (key >= 0 && key < len) {
|
|
if (key + 1 < len) {
|
|
/* move element to be removed to the back */
|
|
memcpy(&tmp, &array[key], sizeof(IDProperty));
|
|
memmove(array + key, array + key + 1, sizeof(IDProperty) * (len - (key + 1)));
|
|
memcpy(&array[len - 1], &tmp, sizeof(IDProperty));
|
|
}
|
|
|
|
IDP_ResizeIDPArray(idprop, len - 1);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
else if (prop->flag & PROP_IDPROPERTY) {
|
|
return true;
|
|
}
|
|
|
|
/* py api calls directly */
|
|
#if 0
|
|
else if (cprop->remove) {
|
|
if (!(cprop->remove->flag & FUNC_USE_CONTEXT)) { /* XXX check for this somewhere else */
|
|
ParameterList params;
|
|
RNA_parameter_list_create(¶ms, ptr, cprop->remove);
|
|
RNA_function_call(NULL, NULL, ptr, cprop->remove, ¶ms);
|
|
RNA_parameter_list_free(¶ms);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
#if 0
|
|
else {
|
|
printf("%s %s.%s: only supported for id properties.\n", __func__, ptr->type->identifier, prop->identifier);
|
|
}
|
|
#endif
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
bool RNA_property_collection_move(PointerRNA *ptr, PropertyRNA *prop, int key, int pos)
|
|
{
|
|
IDProperty *idprop;
|
|
|
|
BLI_assert(RNA_property_type(prop) == PROP_COLLECTION);
|
|
|
|
if ((idprop = rna_idproperty_check(&prop, ptr))) {
|
|
IDProperty tmp, *array;
|
|
int len;
|
|
|
|
len = idprop->len;
|
|
array = IDP_IDPArray(idprop);
|
|
|
|
if (key >= 0 && key < len && pos >= 0 && pos < len && key != pos) {
|
|
memcpy(&tmp, &array[key], sizeof(IDProperty));
|
|
if (pos < key)
|
|
memmove(array + pos + 1, array + pos, sizeof(IDProperty) * (key - pos));
|
|
else
|
|
memmove(array + key, array + key + 1, sizeof(IDProperty) * (pos - key));
|
|
memcpy(&array[pos], &tmp, sizeof(IDProperty));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
else if (prop->flag & PROP_IDPROPERTY) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void RNA_property_collection_clear(PointerRNA *ptr, PropertyRNA *prop)
|
|
{
|
|
IDProperty *idprop;
|
|
|
|
BLI_assert(RNA_property_type(prop) == PROP_COLLECTION);
|
|
|
|
if ((idprop = rna_idproperty_check(&prop, ptr))) {
|
|
IDP_ResizeIDPArray(idprop, 0);
|
|
rna_idproperty_touch(idprop);
|
|
}
|
|
}
|
|
|
|
int RNA_property_collection_lookup_index(PointerRNA *ptr, PropertyRNA *prop, PointerRNA *t_ptr)
|
|
{
|
|
CollectionPropertyIterator iter;
|
|
int index = 0;
|
|
|
|
BLI_assert(RNA_property_type(prop) == PROP_COLLECTION);
|
|
|
|
RNA_property_collection_begin(ptr, prop, &iter);
|
|
for (index = 0; iter.valid; RNA_property_collection_next(&iter), index++) {
|
|
if (iter.ptr.data == t_ptr->data)
|
|
break;
|
|
}
|
|
RNA_property_collection_end(&iter);
|
|
|
|
/* did we find it? */
|
|
if (iter.valid)
|
|
return index;
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
int RNA_property_collection_lookup_int(PointerRNA *ptr, PropertyRNA *prop, int key, PointerRNA *r_ptr)
|
|
{
|
|
CollectionPropertyRNA *cprop = (CollectionPropertyRNA *)rna_ensure_property(prop);
|
|
|
|
BLI_assert(RNA_property_type(prop) == PROP_COLLECTION);
|
|
|
|
if (cprop->lookupint) {
|
|
/* we have a callback defined, use it */
|
|
return cprop->lookupint(ptr, key, r_ptr);
|
|
}
|
|
else {
|
|
/* no callback defined, just iterate and find the nth item */
|
|
CollectionPropertyIterator iter;
|
|
int i;
|
|
|
|
RNA_property_collection_begin(ptr, prop, &iter);
|
|
for (i = 0; iter.valid; RNA_property_collection_next(&iter), i++) {
|
|
if (i == key) {
|
|
*r_ptr = iter.ptr;
|
|
break;
|
|
}
|
|
}
|
|
RNA_property_collection_end(&iter);
|
|
|
|
if (!iter.valid)
|
|
memset(r_ptr, 0, sizeof(*r_ptr));
|
|
|
|
return iter.valid;
|
|
}
|
|
}
|
|
|
|
int RNA_property_collection_lookup_string(PointerRNA *ptr, PropertyRNA *prop, const char *key, PointerRNA *r_ptr)
|
|
{
|
|
CollectionPropertyRNA *cprop = (CollectionPropertyRNA *)rna_ensure_property(prop);
|
|
|
|
BLI_assert(RNA_property_type(prop) == PROP_COLLECTION);
|
|
|
|
if (cprop->lookupstring) {
|
|
/* we have a callback defined, use it */
|
|
return cprop->lookupstring(ptr, key, r_ptr);
|
|
}
|
|
else {
|
|
/* no callback defined, compare with name properties if they exist */
|
|
CollectionPropertyIterator iter;
|
|
PropertyRNA *nameprop;
|
|
char name[256], *nameptr;
|
|
int found = 0;
|
|
int keylen = strlen(key);
|
|
int namelen;
|
|
|
|
RNA_property_collection_begin(ptr, prop, &iter);
|
|
for (; iter.valid; RNA_property_collection_next(&iter)) {
|
|
if (iter.ptr.data && iter.ptr.type->nameproperty) {
|
|
nameprop = iter.ptr.type->nameproperty;
|
|
|
|
nameptr = RNA_property_string_get_alloc(&iter.ptr, nameprop, name, sizeof(name), &namelen);
|
|
|
|
if ((keylen == namelen) && STREQ(nameptr, key)) {
|
|
*r_ptr = iter.ptr;
|
|
found = 1;
|
|
}
|
|
|
|
if ((char *)&name != nameptr)
|
|
MEM_freeN(nameptr);
|
|
|
|
if (found)
|
|
break;
|
|
}
|
|
}
|
|
RNA_property_collection_end(&iter);
|
|
|
|
if (!iter.valid)
|
|
memset(r_ptr, 0, sizeof(*r_ptr));
|
|
|
|
return iter.valid;
|
|
}
|
|
}
|
|
|
|
/* zero return is an assignment error */
|
|
int RNA_property_collection_assign_int(PointerRNA *ptr, PropertyRNA *prop, const int key, const PointerRNA *assign_ptr)
|
|
{
|
|
CollectionPropertyRNA *cprop = (CollectionPropertyRNA *)rna_ensure_property(prop);
|
|
|
|
BLI_assert(RNA_property_type(prop) == PROP_COLLECTION);
|
|
|
|
if (cprop->assignint) {
|
|
/* we have a callback defined, use it */
|
|
return cprop->assignint(ptr, key, assign_ptr);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
bool RNA_property_collection_type_get(PointerRNA *ptr, PropertyRNA *prop, PointerRNA *r_ptr)
|
|
{
|
|
BLI_assert(RNA_property_type(prop) == PROP_COLLECTION);
|
|
|
|
*r_ptr = *ptr;
|
|
return ((r_ptr->type = rna_ensure_property(prop)->srna) ? 1 : 0);
|
|
}
|
|
|
|
int RNA_property_collection_raw_array(PointerRNA *ptr, PropertyRNA *prop, PropertyRNA *itemprop, RawArray *array)
|
|
{
|
|
CollectionPropertyIterator iter;
|
|
ArrayIterator *internal;
|
|
char *arrayp;
|
|
|
|
BLI_assert(RNA_property_type(prop) == PROP_COLLECTION);
|
|
|
|
if (!(prop->flag_internal & PROP_INTERN_RAW_ARRAY) || !(itemprop->flag_internal & PROP_INTERN_RAW_ACCESS))
|
|
return 0;
|
|
|
|
RNA_property_collection_begin(ptr, prop, &iter);
|
|
|
|
if (iter.valid) {
|
|
/* get data from array iterator and item property */
|
|
internal = &iter.internal.array;
|
|
arrayp = (iter.valid) ? iter.ptr.data : NULL;
|
|
|
|
if (internal->skip || !RNA_property_editable(&iter.ptr, itemprop)) {
|
|
/* we might skip some items, so it's not a proper array */
|
|
RNA_property_collection_end(&iter);
|
|
return 0;
|
|
}
|
|
|
|
array->array = arrayp + itemprop->rawoffset;
|
|
array->stride = internal->itemsize;
|
|
array->len = ((char *)internal->endptr - arrayp) / internal->itemsize;
|
|
array->type = itemprop->rawtype;
|
|
}
|
|
else
|
|
memset(array, 0, sizeof(RawArray));
|
|
|
|
RNA_property_collection_end(&iter);
|
|
|
|
return 1;
|
|
}
|
|
|
|
#define RAW_GET(dtype, var, raw, a) \
|
|
{ \
|
|
switch (raw.type) { \
|
|
case PROP_RAW_CHAR: var = (dtype)((char *)raw.array)[a]; break; \
|
|
case PROP_RAW_SHORT: var = (dtype)((short *)raw.array)[a]; break; \
|
|
case PROP_RAW_INT: var = (dtype)((int *)raw.array)[a]; break; \
|
|
case PROP_RAW_BOOLEAN: var = (dtype)((bool *)raw.array)[a]; break; \
|
|
case PROP_RAW_FLOAT: var = (dtype)((float *)raw.array)[a]; break; \
|
|
case PROP_RAW_DOUBLE: var = (dtype)((double *)raw.array)[a]; break; \
|
|
default: var = (dtype)0; \
|
|
} \
|
|
} (void)0
|
|
|
|
#define RAW_SET(dtype, raw, a, var) \
|
|
{ \
|
|
switch (raw.type) { \
|
|
case PROP_RAW_CHAR: ((char *)raw.array)[a] = (char)var; break; \
|
|
case PROP_RAW_SHORT: ((short *)raw.array)[a] = (short)var; break; \
|
|
case PROP_RAW_INT: ((int *)raw.array)[a] = (int)var; break; \
|
|
case PROP_RAW_BOOLEAN: ((bool *)raw.array)[a] = (bool)var; break; \
|
|
case PROP_RAW_FLOAT: ((float *)raw.array)[a] = (float)var; break; \
|
|
case PROP_RAW_DOUBLE: ((double *)raw.array)[a] = (double)var; break; \
|
|
default: break; \
|
|
} \
|
|
} (void)0
|
|
|
|
int RNA_raw_type_sizeof(RawPropertyType type)
|
|
{
|
|
switch (type) {
|
|
case PROP_RAW_CHAR: return sizeof(char);
|
|
case PROP_RAW_SHORT: return sizeof(short);
|
|
case PROP_RAW_INT: return sizeof(int);
|
|
case PROP_RAW_BOOLEAN: return sizeof(bool);
|
|
case PROP_RAW_FLOAT: return sizeof(float);
|
|
case PROP_RAW_DOUBLE: return sizeof(double);
|
|
default: return 0;
|
|
}
|
|
}
|
|
|
|
static int rna_property_array_length_all_dimensions(PointerRNA *ptr, PropertyRNA *prop)
|
|
{
|
|
int i, len[RNA_MAX_ARRAY_DIMENSION];
|
|
const int dim = RNA_property_array_dimension(ptr, prop, len);
|
|
int size;
|
|
|
|
if (dim == 0)
|
|
return 0;
|
|
|
|
for (size = 1, i = 0; i < dim; i++)
|
|
size *= len[i];
|
|
|
|
return size;
|
|
}
|
|
|
|
static int rna_raw_access(ReportList *reports, PointerRNA *ptr, PropertyRNA *prop, const char *propname,
|
|
void *inarray, RawPropertyType intype, int inlen, int set)
|
|
{
|
|
StructRNA *ptype;
|
|
PointerRNA itemptr_base;
|
|
PropertyRNA *itemprop, *iprop;
|
|
PropertyType itemtype = 0;
|
|
RawArray in;
|
|
int itemlen = 0;
|
|
|
|
/* initialize in array, stride assumed 0 in following code */
|
|
in.array = inarray;
|
|
in.type = intype;
|
|
in.len = inlen;
|
|
in.stride = 0;
|
|
|
|
ptype = RNA_property_pointer_type(ptr, prop);
|
|
|
|
/* try to get item property pointer */
|
|
RNA_pointer_create(NULL, ptype, NULL, &itemptr_base);
|
|
itemprop = RNA_struct_find_property(&itemptr_base, propname);
|
|
|
|
if (itemprop) {
|
|
/* we have item property pointer */
|
|
RawArray out;
|
|
|
|
/* check type */
|
|
itemtype = RNA_property_type(itemprop);
|
|
|
|
if (!ELEM(itemtype, PROP_BOOLEAN, PROP_INT, PROP_FLOAT)) {
|
|
BKE_report(reports, RPT_ERROR, "Only boolean, int and float properties supported");
|
|
return 0;
|
|
}
|
|
|
|
/* check item array */
|
|
itemlen = RNA_property_array_length(&itemptr_base, itemprop);
|
|
|
|
/* dynamic array? need to get length per item */
|
|
if (itemprop->getlength) {
|
|
itemprop = NULL;
|
|
}
|
|
/* try to access as raw array */
|
|
else if (RNA_property_collection_raw_array(ptr, prop, itemprop, &out)) {
|
|
int arraylen = (itemlen == 0) ? 1 : itemlen;
|
|
if (in.len != arraylen * out.len) {
|
|
BKE_reportf(reports, RPT_ERROR, "Array length mismatch (expected %d, got %d)",
|
|
out.len * arraylen, in.len);
|
|
return 0;
|
|
}
|
|
|
|
/* matching raw types */
|
|
if (out.type == in.type) {
|
|
void *inp = in.array;
|
|
void *outp = out.array;
|
|
int a, size;
|
|
|
|
size = RNA_raw_type_sizeof(out.type) * arraylen;
|
|
|
|
for (a = 0; a < out.len; a++) {
|
|
if (set) memcpy(outp, inp, size);
|
|
else memcpy(inp, outp, size);
|
|
|
|
inp = (char *)inp + size;
|
|
outp = (char *)outp + out.stride;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* could also be faster with non-matching types,
|
|
* for now we just do slower loop .. */
|
|
}
|
|
}
|
|
|
|
{
|
|
void *tmparray = NULL;
|
|
int tmplen = 0;
|
|
int err = 0, j, a = 0;
|
|
int needconv = 1;
|
|
|
|
if (((itemtype == PROP_INT) && (in.type == PROP_RAW_INT)) ||
|
|
((itemtype == PROP_BOOLEAN) && (in.type == PROP_RAW_BOOLEAN)) ||
|
|
((itemtype == PROP_FLOAT) && (in.type == PROP_RAW_FLOAT)))
|
|
{
|
|
/* avoid creating temporary buffer if the data type match */
|
|
needconv = 0;
|
|
}
|
|
/* no item property pointer, can still be id property, or
|
|
* property of a type derived from the collection pointer type */
|
|
RNA_PROP_BEGIN (ptr, itemptr, prop)
|
|
{
|
|
if (itemptr.data) {
|
|
if (itemprop) {
|
|
/* we got the property already */
|
|
iprop = itemprop;
|
|
}
|
|
else {
|
|
/* not yet, look it up and verify if it is valid */
|
|
iprop = RNA_struct_find_property(&itemptr, propname);
|
|
|
|
if (iprop) {
|
|
itemlen = rna_property_array_length_all_dimensions(&itemptr, iprop);
|
|
itemtype = RNA_property_type(iprop);
|
|
}
|
|
else {
|
|
BKE_reportf(reports, RPT_ERROR, "Property named '%s' not found", propname);
|
|
err = 1;
|
|
break;
|
|
}
|
|
|
|
if (!ELEM(itemtype, PROP_BOOLEAN, PROP_INT, PROP_FLOAT)) {
|
|
BKE_report(reports, RPT_ERROR, "Only boolean, int and float properties supported");
|
|
err = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* editable check */
|
|
if (!set || RNA_property_editable(&itemptr, iprop)) {
|
|
if (a + itemlen > in.len) {
|
|
BKE_reportf(reports, RPT_ERROR, "Array length mismatch (got %d, expected more)", in.len);
|
|
err = 1;
|
|
break;
|
|
}
|
|
|
|
if (itemlen == 0) {
|
|
/* handle conversions */
|
|
if (set) {
|
|
switch (itemtype) {
|
|
case PROP_BOOLEAN:
|
|
{
|
|
int b;
|
|
RAW_GET(bool, b, in, a);
|
|
RNA_property_boolean_set(&itemptr, iprop, b);
|
|
break;
|
|
}
|
|
case PROP_INT:
|
|
{
|
|
int i;
|
|
RAW_GET(int, i, in, a);
|
|
RNA_property_int_set(&itemptr, iprop, i);
|
|
break;
|
|
}
|
|
case PROP_FLOAT:
|
|
{
|
|
float f;
|
|
RAW_GET(float, f, in, a);
|
|
RNA_property_float_set(&itemptr, iprop, f);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
switch (itemtype) {
|
|
case PROP_BOOLEAN:
|
|
{
|
|
int b = RNA_property_boolean_get(&itemptr, iprop);
|
|
RAW_SET(bool, in, a, b);
|
|
break;
|
|
}
|
|
case PROP_INT:
|
|
{
|
|
int i = RNA_property_int_get(&itemptr, iprop);
|
|
RAW_SET(int, in, a, i);
|
|
break;
|
|
}
|
|
case PROP_FLOAT:
|
|
{
|
|
float f = RNA_property_float_get(&itemptr, iprop);
|
|
RAW_SET(float, in, a, f);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
a++;
|
|
}
|
|
else if (needconv == 1) {
|
|
/* allocate temporary array if needed */
|
|
if (tmparray && tmplen != itemlen) {
|
|
MEM_freeN(tmparray);
|
|
tmparray = NULL;
|
|
}
|
|
if (!tmparray) {
|
|
tmparray = MEM_callocN(sizeof(float) * itemlen, "RNA tmparray");
|
|
tmplen = itemlen;
|
|
}
|
|
|
|
/* handle conversions */
|
|
if (set) {
|
|
switch (itemtype) {
|
|
case PROP_BOOLEAN:
|
|
{
|
|
for (j = 0; j < itemlen; j++, a++)
|
|
RAW_GET(bool, ((bool *)tmparray)[j], in, a);
|
|
RNA_property_boolean_set_array(&itemptr, iprop, tmparray);
|
|
break;
|
|
}
|
|
case PROP_INT:
|
|
{
|
|
for (j = 0; j < itemlen; j++, a++)
|
|
RAW_GET(int, ((int *)tmparray)[j], in, a);
|
|
RNA_property_int_set_array(&itemptr, iprop, tmparray);
|
|
break;
|
|
}
|
|
case PROP_FLOAT:
|
|
{
|
|
for (j = 0; j < itemlen; j++, a++)
|
|
RAW_GET(float, ((float *)tmparray)[j], in, a);
|
|
RNA_property_float_set_array(&itemptr, iprop, tmparray);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
switch (itemtype) {
|
|
case PROP_BOOLEAN:
|
|
{
|
|
RNA_property_boolean_get_array(&itemptr, iprop, tmparray);
|
|
for (j = 0; j < itemlen; j++, a++)
|
|
RAW_SET(int, in, a, ((bool *)tmparray)[j]);
|
|
break;
|
|
}
|
|
case PROP_INT:
|
|
{
|
|
RNA_property_int_get_array(&itemptr, iprop, tmparray);
|
|
for (j = 0; j < itemlen; j++, a++)
|
|
RAW_SET(int, in, a, ((int *)tmparray)[j]);
|
|
break;
|
|
}
|
|
case PROP_FLOAT:
|
|
{
|
|
RNA_property_float_get_array(&itemptr, iprop, tmparray);
|
|
for (j = 0; j < itemlen; j++, a++)
|
|
RAW_SET(float, in, a, ((float *)tmparray)[j]);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if (set) {
|
|
switch (itemtype) {
|
|
case PROP_BOOLEAN:
|
|
{
|
|
RNA_property_boolean_set_array(&itemptr, iprop, &((bool *)in.array)[a]);
|
|
a += itemlen;
|
|
break;
|
|
}
|
|
case PROP_INT:
|
|
{
|
|
RNA_property_int_set_array(&itemptr, iprop, &((int *)in.array)[a]);
|
|
a += itemlen;
|
|
break;
|
|
}
|
|
case PROP_FLOAT:
|
|
{
|
|
RNA_property_float_set_array(&itemptr, iprop, &((float *)in.array)[a]);
|
|
a += itemlen;
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
switch (itemtype) {
|
|
case PROP_BOOLEAN:
|
|
{
|
|
RNA_property_boolean_get_array(&itemptr, iprop, &((bool *)in.array)[a]);
|
|
a += itemlen;
|
|
break;
|
|
}
|
|
case PROP_INT:
|
|
{
|
|
RNA_property_int_get_array(&itemptr, iprop, &((int *)in.array)[a]);
|
|
a += itemlen;
|
|
break;
|
|
}
|
|
case PROP_FLOAT:
|
|
{
|
|
RNA_property_float_get_array(&itemptr, iprop, &((float *)in.array)[a]);
|
|
a += itemlen;
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
RNA_PROP_END;
|
|
|
|
if (tmparray)
|
|
MEM_freeN(tmparray);
|
|
|
|
return !err;
|
|
}
|
|
}
|
|
|
|
RawPropertyType RNA_property_raw_type(PropertyRNA *prop)
|
|
{
|
|
if (prop->rawtype == PROP_RAW_UNSET) {
|
|
/* this property has no raw access, yet we try to provide a raw type to help building the array */
|
|
switch (prop->type) {
|
|
case PROP_BOOLEAN:
|
|
return PROP_RAW_BOOLEAN;
|
|
case PROP_INT:
|
|
return PROP_RAW_INT;
|
|
case PROP_FLOAT:
|
|
return PROP_RAW_FLOAT;
|
|
case PROP_ENUM:
|
|
return PROP_RAW_INT;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return prop->rawtype;
|
|
}
|
|
|
|
int RNA_property_collection_raw_get(ReportList *reports, PointerRNA *ptr, PropertyRNA *prop, const char *propname,
|
|
void *array, RawPropertyType type, int len)
|
|
{
|
|
return rna_raw_access(reports, ptr, prop, propname, array, type, len, 0);
|
|
}
|
|
|
|
int RNA_property_collection_raw_set(ReportList *reports, PointerRNA *ptr, PropertyRNA *prop, const char *propname,
|
|
void *array, RawPropertyType type, int len)
|
|
{
|
|
return rna_raw_access(reports, ptr, prop, propname, array, type, len, 1);
|
|
}
|
|
|
|
/* Standard iterator functions */
|
|
|
|
void rna_iterator_listbase_begin(CollectionPropertyIterator *iter, ListBase *lb, IteratorSkipFunc skip)
|
|
{
|
|
ListBaseIterator *internal = &iter->internal.listbase;
|
|
|
|
internal->link = (lb) ? lb->first : NULL;
|
|
internal->skip = skip;
|
|
|
|
iter->valid = (internal->link != NULL);
|
|
|
|
if (skip && iter->valid && skip(iter, internal->link))
|
|
rna_iterator_listbase_next(iter);
|
|
}
|
|
|
|
void rna_iterator_listbase_next(CollectionPropertyIterator *iter)
|
|
{
|
|
ListBaseIterator *internal = &iter->internal.listbase;
|
|
|
|
if (internal->skip) {
|
|
do {
|
|
internal->link = internal->link->next;
|
|
iter->valid = (internal->link != NULL);
|
|
} while (iter->valid && internal->skip(iter, internal->link));
|
|
}
|
|
else {
|
|
internal->link = internal->link->next;
|
|
iter->valid = (internal->link != NULL);
|
|
}
|
|
}
|
|
|
|
void *rna_iterator_listbase_get(CollectionPropertyIterator *iter)
|
|
{
|
|
ListBaseIterator *internal = &iter->internal.listbase;
|
|
|
|
return internal->link;
|
|
}
|
|
|
|
void rna_iterator_listbase_end(CollectionPropertyIterator *UNUSED(iter))
|
|
{
|
|
}
|
|
|
|
PointerRNA rna_listbase_lookup_int(PointerRNA *ptr, StructRNA *type, struct ListBase *lb, int index)
|
|
{
|
|
void *data = BLI_findlink(lb, index);
|
|
return rna_pointer_inherit_refine(ptr, type, data);
|
|
}
|
|
|
|
void rna_iterator_array_begin(CollectionPropertyIterator *iter, void *ptr, int itemsize, int length,
|
|
bool free_ptr, IteratorSkipFunc skip)
|
|
{
|
|
ArrayIterator *internal;
|
|
|
|
if (ptr == NULL)
|
|
length = 0;
|
|
else if (length == 0) {
|
|
ptr = NULL;
|
|
itemsize = 0;
|
|
}
|
|
|
|
internal = &iter->internal.array;
|
|
internal->ptr = ptr;
|
|
internal->free_ptr = free_ptr ? ptr : NULL;
|
|
internal->endptr = ((char *)ptr) + length * itemsize;
|
|
internal->itemsize = itemsize;
|
|
internal->skip = skip;
|
|
internal->length = length;
|
|
|
|
iter->valid = (internal->ptr != internal->endptr);
|
|
|
|
if (skip && iter->valid && skip(iter, internal->ptr))
|
|
rna_iterator_array_next(iter);
|
|
}
|
|
|
|
void rna_iterator_array_next(CollectionPropertyIterator *iter)
|
|
{
|
|
ArrayIterator *internal = &iter->internal.array;
|
|
|
|
if (internal->skip) {
|
|
do {
|
|
internal->ptr += internal->itemsize;
|
|
iter->valid = (internal->ptr != internal->endptr);
|
|
} while (iter->valid && internal->skip(iter, internal->ptr));
|
|
}
|
|
else {
|
|
internal->ptr += internal->itemsize;
|
|
iter->valid = (internal->ptr != internal->endptr);
|
|
}
|
|
}
|
|
|
|
void *rna_iterator_array_get(CollectionPropertyIterator *iter)
|
|
{
|
|
ArrayIterator *internal = &iter->internal.array;
|
|
|
|
return internal->ptr;
|
|
}
|
|
|
|
void *rna_iterator_array_dereference_get(CollectionPropertyIterator *iter)
|
|
{
|
|
ArrayIterator *internal = &iter->internal.array;
|
|
|
|
/* for ** arrays */
|
|
return *(void **)(internal->ptr);
|
|
}
|
|
|
|
void rna_iterator_array_end(CollectionPropertyIterator *iter)
|
|
{
|
|
ArrayIterator *internal = &iter->internal.array;
|
|
|
|
if (internal->free_ptr) {
|
|
MEM_freeN(internal->free_ptr);
|
|
internal->free_ptr = NULL;
|
|
}
|
|
}
|
|
|
|
PointerRNA rna_array_lookup_int(PointerRNA *ptr, StructRNA *type, void *data, int itemsize, int length, int index)
|
|
{
|
|
if (index < 0 || index >= length)
|
|
return PointerRNA_NULL;
|
|
|
|
return rna_pointer_inherit_refine(ptr, type, ((char *)data) + index * itemsize);
|
|
}
|
|
|
|
/* RNA Path - Experiment */
|
|
|
|
static char *rna_path_token(const char **path, char *fixedbuf, int fixedlen, int bracket)
|
|
{
|
|
const char *p;
|
|
char *buf;
|
|
char quote = '\0';
|
|
int i, j, len, escape;
|
|
|
|
len = 0;
|
|
|
|
if (bracket) {
|
|
/* get data between [], check escaping ] with \] */
|
|
if (**path == '[') (*path)++;
|
|
else return NULL;
|
|
|
|
p = *path;
|
|
|
|
/* 2 kinds of lookups now, quoted or unquoted */
|
|
quote = *p;
|
|
|
|
if (quote != '"') /* " - this comment is hack for Aligorith's text editor's sanity */
|
|
quote = 0;
|
|
|
|
if (quote == 0) {
|
|
while (*p && (*p != ']')) {
|
|
len++;
|
|
p++;
|
|
}
|
|
}
|
|
else {
|
|
escape = 0;
|
|
/* skip the first quote */
|
|
len++;
|
|
p++;
|
|
while (*p && (*p != quote || escape)) {
|
|
escape = (*p == '\\');
|
|
len++;
|
|
p++;
|
|
}
|
|
|
|
/* skip the last quoted char to get the ']' */
|
|
len++;
|
|
p++;
|
|
}
|
|
|
|
if (*p != ']') return NULL;
|
|
}
|
|
else {
|
|
/* get data until . or [ */
|
|
p = *path;
|
|
|
|
while (*p && *p != '.' && *p != '[') {
|
|
len++;
|
|
p++;
|
|
}
|
|
}
|
|
|
|
/* empty, return */
|
|
if (len == 0)
|
|
return NULL;
|
|
|
|
/* try to use fixed buffer if possible */
|
|
if (len + 1 < fixedlen)
|
|
buf = fixedbuf;
|
|
else
|
|
buf = MEM_mallocN(sizeof(char) * (len + 1), "rna_path_token");
|
|
|
|
/* copy string, taking into account escaped ] */
|
|
if (bracket) {
|
|
for (p = *path, i = 0, j = 0; i < len; i++, p++) {
|
|
if (*p == '\\' && *(p + 1) == quote) {}
|
|
else buf[j++] = *p;
|
|
}
|
|
|
|
buf[j] = 0;
|
|
}
|
|
else {
|
|
memcpy(buf, *path, sizeof(char) * len);
|
|
buf[len] = '\0';
|
|
}
|
|
|
|
/* set path to start of next token */
|
|
if (*p == ']') p++;
|
|
if (*p == '.') p++;
|
|
*path = p;
|
|
|
|
return buf;
|
|
}
|
|
|
|
static int rna_token_strip_quotes(char *token)
|
|
{
|
|
if (token[0] == '"') {
|
|
int len = strlen(token);
|
|
if (len >= 2 && token[len - 1] == '"') {
|
|
/* strip away "" */
|
|
token[len - 1] = '\0';
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static bool rna_path_parse_collection_key(const char **path, PointerRNA *ptr, PropertyRNA *prop, PointerRNA *r_nextptr)
|
|
{
|
|
char fixedbuf[256];
|
|
int intkey;
|
|
|
|
*r_nextptr = *ptr;
|
|
|
|
/* end of path, ok */
|
|
if (!(**path))
|
|
return true;
|
|
|
|
if (**path == '[') {
|
|
char *token;
|
|
|
|
/* resolve the lookup with [] brackets */
|
|
token = rna_path_token(path, fixedbuf, sizeof(fixedbuf), 1);
|
|
|
|
if (!token)
|
|
return false;
|
|
|
|
/* check for "" to see if it is a string */
|
|
if (rna_token_strip_quotes(token)) {
|
|
if (RNA_property_collection_lookup_string(ptr, prop, token + 1, r_nextptr)) {
|
|
/* pass */
|
|
}
|
|
else {
|
|
r_nextptr->data = NULL;
|
|
}
|
|
}
|
|
else {
|
|
/* otherwise do int lookup */
|
|
intkey = atoi(token);
|
|
if (intkey == 0 && (token[0] != '0' || token[1] != '\0')) {
|
|
return false; /* we can be sure the fixedbuf was used in this case */
|
|
}
|
|
if (RNA_property_collection_lookup_int(ptr, prop, intkey, r_nextptr)) {
|
|
/* pass */
|
|
}
|
|
else {
|
|
r_nextptr->data = NULL;
|
|
}
|
|
}
|
|
|
|
if (token != fixedbuf) {
|
|
MEM_freeN(token);
|
|
}
|
|
}
|
|
else {
|
|
if (RNA_property_collection_type_get(ptr, prop, r_nextptr)) {
|
|
/* pass */
|
|
}
|
|
else {
|
|
/* ensure we quit on invalid values */
|
|
r_nextptr->data = NULL;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool rna_path_parse_array_index(const char **path, PointerRNA *ptr, PropertyRNA *prop, int *r_index)
|
|
{
|
|
char fixedbuf[256];
|
|
int index_arr[RNA_MAX_ARRAY_DIMENSION] = {0};
|
|
int len[RNA_MAX_ARRAY_DIMENSION];
|
|
const int dim = RNA_property_array_dimension(ptr, prop, len);
|
|
int i;
|
|
|
|
*r_index = -1;
|
|
|
|
/* end of path, ok */
|
|
if (!(**path))
|
|
return true;
|
|
|
|
for (i = 0; i < dim; i++) {
|
|
int temp_index = -1;
|
|
char *token;
|
|
|
|
/* multi index resolve */
|
|
if (**path == '[') {
|
|
token = rna_path_token(path, fixedbuf, sizeof(fixedbuf), 1);
|
|
|
|
if (token == NULL) {
|
|
/* invalid syntax blah[] */
|
|
return false;
|
|
}
|
|
/* check for "" to see if it is a string */
|
|
else if (rna_token_strip_quotes(token)) {
|
|
temp_index = RNA_property_array_item_index(prop, *(token + 1));
|
|
}
|
|
else {
|
|
/* otherwise do int lookup */
|
|
temp_index = atoi(token);
|
|
|
|
if (temp_index == 0 && (token[0] != '0' || token[1] != '\0')) {
|
|
if (token != fixedbuf) {
|
|
MEM_freeN(token);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
else if (dim == 1) {
|
|
/* location.x || scale.X, single dimension arrays only */
|
|
token = rna_path_token(path, fixedbuf, sizeof(fixedbuf), 0);
|
|
if (token == NULL) {
|
|
/* invalid syntax blah.. */
|
|
return false;
|
|
}
|
|
temp_index = RNA_property_array_item_index(prop, *token);
|
|
}
|
|
else {
|
|
/* just to avoid uninitialized pointer use */
|
|
token = fixedbuf;
|
|
}
|
|
|
|
if (token != fixedbuf) {
|
|
MEM_freeN(token);
|
|
}
|
|
|
|
/* out of range */
|
|
if (temp_index < 0 || temp_index >= len[i])
|
|
return false;
|
|
|
|
index_arr[i] = temp_index;
|
|
/* end multi index resolve */
|
|
}
|
|
|
|
/* arrays always contain numbers so further values are not valid */
|
|
if (**path)
|
|
return false;
|
|
|
|
/* flatten index over all dimensions */
|
|
{
|
|
int totdim = 1;
|
|
int flat_index = 0;
|
|
|
|
for (i = dim - 1; i >= 0; i--) {
|
|
flat_index += index_arr[i] * totdim;
|
|
totdim *= len[i];
|
|
}
|
|
|
|
*r_index = flat_index;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Generic rna path parser.
|
|
*
|
|
* \note All parameters besides \a ptr and \a path are optional.
|
|
*
|
|
* \param ptr: The root of given RNA path.
|
|
* \param path: The RNA path.
|
|
* \param r_ptr: The final RNA data holding the last property in \a path.
|
|
* \param r_prop: The final property of \a r_ptr, from \a path.
|
|
* \param r_index: The final index in the \a r_prop, if defined by \a path.
|
|
* \param r_item_ptr: Only valid for Pointer and Collection, return the actual value of the pointer,
|
|
* or of the collection item. Mutually exclusive with \a eval_pointer option.
|
|
* \param r_elements: A list of \a PropertyElemRNA items
|
|
* (pairs of \a PointerRNA, \a PropertyRNA that represent the whole given \a path).
|
|
* \param eval_pointer: If \a true, and \a path leads to a Pointer property, or an item in a Collection property,
|
|
* \a r_ptr will be set to the value of that property, and \a r_prop will be NULL.
|
|
* Mutually exclusive with \a r_item_ptr.
|
|
* \return \a true on success, \a false if the path is somehow invalid.
|
|
*/
|
|
static bool rna_path_parse(PointerRNA *ptr, const char *path,
|
|
PointerRNA *r_ptr, PropertyRNA **r_prop, int *r_index,
|
|
PointerRNA *r_item_ptr, ListBase *r_elements,
|
|
const bool eval_pointer)
|
|
{
|
|
BLI_assert(r_item_ptr == NULL || !eval_pointer);
|
|
PropertyRNA *prop;
|
|
PointerRNA curptr, nextptr;
|
|
PropertyElemRNA *prop_elem = NULL;
|
|
int index = -1;
|
|
char fixedbuf[256];
|
|
int type;
|
|
const bool do_item_ptr = r_item_ptr != NULL && !eval_pointer;
|
|
|
|
if (do_item_ptr) {
|
|
RNA_POINTER_INVALIDATE(&nextptr);
|
|
}
|
|
|
|
prop = NULL;
|
|
curptr = *ptr;
|
|
|
|
if (path == NULL || *path == '\0')
|
|
return false;
|
|
|
|
while (*path) {
|
|
if (do_item_ptr) {
|
|
RNA_POINTER_INVALIDATE(&nextptr);
|
|
}
|
|
|
|
int use_id_prop = (*path == '[') ? 1 : 0;
|
|
char *token;
|
|
/* custom property lookup ?
|
|
* C.object["someprop"]
|
|
*/
|
|
|
|
if (!curptr.data) {
|
|
return false;
|
|
}
|
|
|
|
/* look up property name in current struct */
|
|
token = rna_path_token(&path, fixedbuf, sizeof(fixedbuf), use_id_prop);
|
|
|
|
if (!token) {
|
|
return false;
|
|
}
|
|
|
|
prop = NULL;
|
|
if (use_id_prop) { /* look up property name in current struct */
|
|
IDProperty *group = RNA_struct_idprops(&curptr, 0);
|
|
if (group && rna_token_strip_quotes(token))
|
|
prop = (PropertyRNA *)IDP_GetPropertyFromGroup(group, token + 1);
|
|
}
|
|
else {
|
|
prop = RNA_struct_find_property(&curptr, token);
|
|
}
|
|
|
|
if (token != fixedbuf) {
|
|
MEM_freeN(token);
|
|
}
|
|
|
|
if (!prop) {
|
|
return false;
|
|
}
|
|
|
|
if (r_elements) {
|
|
prop_elem = MEM_mallocN(sizeof(PropertyElemRNA), __func__);
|
|
prop_elem->ptr = curptr;
|
|
prop_elem->prop = prop;
|
|
prop_elem->index = -1; /* index will be added later, if needed. */
|
|
BLI_addtail(r_elements, prop_elem);
|
|
}
|
|
|
|
type = RNA_property_type(prop);
|
|
|
|
/* now look up the value of this property if it is a pointer or
|
|
* collection, otherwise return the property rna so that the
|
|
* caller can read the value of the property itself */
|
|
switch (type) {
|
|
case PROP_POINTER: {
|
|
/* resolve pointer if further path elements follow
|
|
* or explicitly requested
|
|
*/
|
|
if (do_item_ptr || eval_pointer || *path != '\0') {
|
|
nextptr = RNA_property_pointer_get(&curptr, prop);
|
|
}
|
|
|
|
if (eval_pointer || *path != '\0') {
|
|
curptr = nextptr;
|
|
prop = NULL; /* now we have a PointerRNA, the prop is our parent so forget it */
|
|
index = -1;
|
|
}
|
|
break;
|
|
}
|
|
case PROP_COLLECTION: {
|
|
/* Resolve pointer if further path elements follow.
|
|
* Note that if path is empty, rna_path_parse_collection_key will do nothing anyway,
|
|
* so do_item_ptr is of no use in that case.
|
|
*/
|
|
if (*path) {
|
|
if (!rna_path_parse_collection_key(&path, &curptr, prop, &nextptr)) {
|
|
return false;
|
|
}
|
|
|
|
if (eval_pointer || *path != '\0') {
|
|
curptr = nextptr;
|
|
prop = NULL; /* now we have a PointerRNA, the prop is our parent so forget it */
|
|
index = -1;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
if (r_index || prop_elem) {
|
|
if (!rna_path_parse_array_index(&path, &curptr, prop, &index)) {
|
|
return false;
|
|
}
|
|
|
|
if (prop_elem) {
|
|
prop_elem->index = index;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (r_ptr) {
|
|
*r_ptr = curptr;
|
|
}
|
|
if (r_prop) {
|
|
*r_prop = prop;
|
|
}
|
|
if (r_index) {
|
|
*r_index = index;
|
|
}
|
|
if (r_item_ptr && do_item_ptr) {
|
|
*r_item_ptr = nextptr;
|
|
}
|
|
|
|
if (prop_elem && (prop_elem->ptr.data != curptr.data || prop_elem->prop != prop || prop_elem->index != index)) {
|
|
prop_elem = MEM_mallocN(sizeof(PropertyElemRNA), __func__);
|
|
prop_elem->ptr = curptr;
|
|
prop_elem->prop = prop;
|
|
prop_elem->index = index;
|
|
BLI_addtail(r_elements, prop_elem);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Resolve the given RNA Path to find the pointer and/or property indicated by fully resolving the path.
|
|
*
|
|
* \note Assumes all pointers provided are valid
|
|
* \return True if path can be resolved to a valid "pointer + property" OR "pointer only"
|
|
*/
|
|
bool RNA_path_resolve(PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop)
|
|
{
|
|
if (!rna_path_parse(ptr, path, r_ptr, r_prop, NULL, NULL, NULL, true))
|
|
return false;
|
|
|
|
return r_ptr->data != NULL;
|
|
}
|
|
|
|
/**
|
|
* Resolve the given RNA Path to find the pointer and/or property + array index indicated by fully resolving the path.
|
|
*
|
|
* \note Assumes all pointers provided are valid.
|
|
* \return True if path can be resolved to a valid "pointer + property" OR "pointer only"
|
|
*/
|
|
bool RNA_path_resolve_full(PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop, int *r_index)
|
|
{
|
|
if (!rna_path_parse(ptr, path, r_ptr, r_prop, r_index, NULL, NULL, true))
|
|
return false;
|
|
|
|
return r_ptr->data != NULL;
|
|
}
|
|
|
|
/**
|
|
* Resolve the given RNA Path to find both the pointer AND property indicated by fully resolving the path.
|
|
*
|
|
* This is a convenience method to avoid logic errors and ugly syntax.
|
|
* \note Assumes all pointers provided are valid
|
|
* \return True only if both a valid pointer and property are found after resolving the path
|
|
*/
|
|
bool RNA_path_resolve_property(PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop)
|
|
{
|
|
if (!rna_path_parse(ptr, path, r_ptr, r_prop, NULL, NULL, NULL, false)) {
|
|
return false;
|
|
}
|
|
|
|
return r_ptr->data != NULL && *r_prop != NULL;
|
|
}
|
|
|
|
/**
|
|
* Resolve the given RNA Path to find the pointer AND property (as well as the array index)
|
|
* indicated by fully resolving the path.
|
|
*
|
|
* This is a convenience method to avoid logic errors and ugly syntax.
|
|
* \note Assumes all pointers provided are valid
|
|
* \return True only if both a valid pointer and property are found after resolving the path
|
|
*/
|
|
bool RNA_path_resolve_property_full(PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop, int *r_index)
|
|
{
|
|
if (!rna_path_parse(ptr, path, r_ptr, r_prop, r_index, NULL, NULL, false))
|
|
return false;
|
|
|
|
return r_ptr->data != NULL && *r_prop != NULL;
|
|
}
|
|
|
|
/**
|
|
* Resolve the given RNA Path to find both the pointer AND property indicated by fully resolving the path,
|
|
* and get the value of the Pointer property (or item of the collection).
|
|
*
|
|
* This is a convenience method to avoid logic errors and ugly syntax, it combines both \a RNA_path_resolve and
|
|
* \a RNA_path_resolve_property in a single call.
|
|
* \note Assumes all pointers provided are valid.
|
|
* \param r_item_pointer: The final Pointer or Collection item value. You must check for its validity before use!
|
|
* \return True only if both a valid pointer and property are found after resolving the path
|
|
*/
|
|
bool RNA_path_resolve_property_and_item_pointer(
|
|
PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop, PointerRNA *r_item_ptr)
|
|
{
|
|
if (!rna_path_parse(ptr, path, r_ptr, r_prop, NULL, r_item_ptr, NULL, false)) {
|
|
return false;
|
|
}
|
|
|
|
return r_ptr->data != NULL && *r_prop != NULL;
|
|
}
|
|
|
|
/**
|
|
* Resolve the given RNA Path to find both the pointer AND property (as well as the array index)
|
|
* indicated by fully resolving the path, and get the value of the Pointer property (or item of the collection).
|
|
*
|
|
* This is a convenience method to avoid logic errors and ugly syntax, it combines both \a RNA_path_resolve_full and
|
|
* \a RNA_path_resolve_property_full in a single call.
|
|
* \note Assumes all pointers provided are valid.
|
|
* \param r_item_pointer: The final Pointer or Collection item value. You must check for its validity before use!
|
|
* \return True only if both a valid pointer and property are found after resolving the path
|
|
*/
|
|
bool RNA_path_resolve_property_and_item_pointer_full(
|
|
PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop, int *r_index, PointerRNA *r_item_ptr)
|
|
{
|
|
if (!rna_path_parse(ptr, path, r_ptr, r_prop, r_index, r_item_ptr, NULL, false))
|
|
return false;
|
|
|
|
return r_ptr->data != NULL && *r_prop != NULL;
|
|
}
|
|
/**
|
|
* Resolve the given RNA Path into a linked list of PropertyElemRNA's.
|
|
*
|
|
* To be used when complex operations over path are needed, like e.g. get relative paths, to avoid too much
|
|
* string operations.
|
|
*
|
|
* \return True if there was no error while resolving the path
|
|
* \note Assumes all pointers provided are valid
|
|
*/
|
|
bool RNA_path_resolve_elements(PointerRNA *ptr, const char *path, ListBase *r_elements)
|
|
{
|
|
return rna_path_parse(ptr, path, NULL, NULL, NULL, NULL, r_elements, false);
|
|
}
|
|
|
|
char *RNA_path_append(const char *path, PointerRNA *UNUSED(ptr), PropertyRNA *prop, int intkey, const char *strkey)
|
|
{
|
|
DynStr *dynstr;
|
|
const char *s;
|
|
char appendstr[128], *result;
|
|
|
|
dynstr = BLI_dynstr_new();
|
|
|
|
/* add .identifier */
|
|
if (path) {
|
|
BLI_dynstr_append(dynstr, path);
|
|
if (*path)
|
|
BLI_dynstr_append(dynstr, ".");
|
|
}
|
|
|
|
BLI_dynstr_append(dynstr, RNA_property_identifier(prop));
|
|
|
|
if (RNA_property_type(prop) == PROP_COLLECTION) {
|
|
/* add ["strkey"] or [intkey] */
|
|
BLI_dynstr_append(dynstr, "[");
|
|
|
|
if (strkey) {
|
|
BLI_dynstr_append(dynstr, "\"");
|
|
for (s = strkey; *s; s++) {
|
|
if (*s == '[') {
|
|
appendstr[0] = '\\';
|
|
appendstr[1] = *s;
|
|
appendstr[2] = 0;
|
|
}
|
|
else {
|
|
appendstr[0] = *s;
|
|
appendstr[1] = 0;
|
|
}
|
|
BLI_dynstr_append(dynstr, appendstr);
|
|
}
|
|
BLI_dynstr_append(dynstr, "\"");
|
|
}
|
|
else {
|
|
BLI_snprintf(appendstr, sizeof(appendstr), "%d", intkey);
|
|
BLI_dynstr_append(dynstr, appendstr);
|
|
}
|
|
|
|
BLI_dynstr_append(dynstr, "]");
|
|
}
|
|
|
|
result = BLI_dynstr_get_cstring(dynstr);
|
|
BLI_dynstr_free(dynstr);
|
|
|
|
return result;
|
|
}
|
|
|
|
char *RNA_path_back(const char *path)
|
|
{
|
|
char fixedbuf[256];
|
|
const char *previous, *current;
|
|
char *result;
|
|
int i;
|
|
|
|
if (!path)
|
|
return NULL;
|
|
|
|
previous = NULL;
|
|
current = path;
|
|
|
|
/* parse token by token until the end, then we back up to the previous
|
|
* position and strip of the next token to get the path one step back */
|
|
while (*current) {
|
|
char *token;
|
|
|
|
token = rna_path_token(¤t, fixedbuf, sizeof(fixedbuf), 0);
|
|
|
|
if (!token)
|
|
return NULL;
|
|
if (token != fixedbuf)
|
|
MEM_freeN(token);
|
|
|
|
/* in case of collection we also need to strip off [] */
|
|
token = rna_path_token(¤t, fixedbuf, sizeof(fixedbuf), 1);
|
|
if (token && token != fixedbuf)
|
|
MEM_freeN(token);
|
|
|
|
if (!*current)
|
|
break;
|
|
|
|
previous = current;
|
|
}
|
|
|
|
if (!previous)
|
|
return NULL;
|
|
|
|
/* copy and strip off last token */
|
|
i = previous - path;
|
|
result = BLI_strdup(path);
|
|
|
|
if (i > 0 && result[i - 1] == '.') i--;
|
|
result[i] = 0;
|
|
|
|
return result;
|
|
}
|
|
|
|
/* generic path search func
|
|
* if its needed this could also reference the IDProperty direct */
|
|
typedef struct IDP_Chain {
|
|
struct IDP_Chain *up; /* parent member, reverse and set to child for path conversion. */
|
|
|
|
const char *name;
|
|
int index;
|
|
|
|
} IDP_Chain;
|
|
|
|
static char *rna_idp_path_create(IDP_Chain *child_link)
|
|
{
|
|
DynStr *dynstr = BLI_dynstr_new();
|
|
char *path;
|
|
bool is_first = true;
|
|
|
|
int tot = 0;
|
|
IDP_Chain *link = child_link;
|
|
|
|
/* reverse the list */
|
|
IDP_Chain *link_prev;
|
|
link_prev = NULL;
|
|
while (link) {
|
|
IDP_Chain *link_next = link->up;
|
|
link->up = link_prev;
|
|
link_prev = link;
|
|
link = link_next;
|
|
tot++;
|
|
}
|
|
|
|
for (link = link_prev; link; link = link->up) {
|
|
/* pass */
|
|
if (link->index >= 0) {
|
|
BLI_dynstr_appendf(dynstr, is_first ? "%s[%d]" : ".%s[%d]", link->name, link->index);
|
|
}
|
|
else {
|
|
BLI_dynstr_appendf(dynstr, is_first ? "%s" : ".%s", link->name);
|
|
}
|
|
|
|
is_first = false;
|
|
}
|
|
|
|
path = BLI_dynstr_get_cstring(dynstr);
|
|
BLI_dynstr_free(dynstr);
|
|
|
|
if (*path == '\0') {
|
|
MEM_freeN(path);
|
|
path = NULL;
|
|
}
|
|
|
|
return path;
|
|
}
|
|
|
|
static char *rna_idp_path(PointerRNA *ptr, IDProperty *haystack, IDProperty *needle, IDP_Chain *parent_link)
|
|
{
|
|
char *path = NULL;
|
|
IDP_Chain link;
|
|
|
|
IDProperty *iter;
|
|
int i;
|
|
|
|
BLI_assert(haystack->type == IDP_GROUP);
|
|
|
|
link.up = parent_link;
|
|
/* always set both name and index,
|
|
* else a stale value might get used */
|
|
link.name = NULL;
|
|
link.index = -1;
|
|
|
|
for (i = 0, iter = haystack->data.group.first; iter; iter = iter->next, i++) {
|
|
if (needle == iter) { /* found! */
|
|
link.name = iter->name;
|
|
link.index = -1;
|
|
path = rna_idp_path_create(&link);
|
|
break;
|
|
}
|
|
else {
|
|
if (iter->type == IDP_GROUP) {
|
|
/* ensure this is RNA */
|
|
PropertyRNA *prop = RNA_struct_find_property(ptr, iter->name);
|
|
if (prop && prop->type == PROP_POINTER) {
|
|
PointerRNA child_ptr = RNA_property_pointer_get(ptr, prop);
|
|
link.name = iter->name;
|
|
link.index = -1;
|
|
if ((path = rna_idp_path(&child_ptr, iter, needle, &link))) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if (iter->type == IDP_IDPARRAY) {
|
|
PropertyRNA *prop = RNA_struct_find_property(ptr, iter->name);
|
|
if (prop && prop->type == PROP_COLLECTION) {
|
|
IDProperty *array = IDP_IDPArray(iter);
|
|
if (needle >= array && needle < (iter->len + array)) { /* found! */
|
|
link.name = iter->name;
|
|
link.index = (int)(needle - array);
|
|
path = rna_idp_path_create(&link);
|
|
break;
|
|
}
|
|
else {
|
|
int j;
|
|
link.name = iter->name;
|
|
for (j = 0; j < iter->len; j++, array++) {
|
|
PointerRNA child_ptr;
|
|
if (RNA_property_collection_lookup_int(ptr, prop, j, &child_ptr)) {
|
|
link.index = j;
|
|
if ((path = rna_idp_path(&child_ptr, array, needle, &link))) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (path)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return path;
|
|
}
|
|
|
|
static char *rna_path_from_ID_to_idpgroup(PointerRNA *ptr)
|
|
{
|
|
PointerRNA id_ptr;
|
|
IDProperty *haystack;
|
|
IDProperty *needle;
|
|
|
|
BLI_assert(ptr->id.data != NULL);
|
|
|
|
/* TODO, Support Bones/PoseBones. no pointers stored to the bones from here, only the ID. See example in [#25746]
|
|
* Unless this is added only way to find this is to also search all bones and pose bones
|
|
* of an armature or object */
|
|
RNA_id_pointer_create(ptr->id.data, &id_ptr);
|
|
|
|
haystack = RNA_struct_idprops(&id_ptr, false);
|
|
if (haystack) { /* can fail when called on bones */
|
|
needle = ptr->data;
|
|
return rna_idp_path(&id_ptr, haystack, needle, NULL);
|
|
}
|
|
else {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
char *RNA_path_from_ID_to_struct(PointerRNA *ptr)
|
|
{
|
|
char *ptrpath = NULL;
|
|
|
|
if (!ptr->id.data || !ptr->data)
|
|
return NULL;
|
|
|
|
if (!RNA_struct_is_ID(ptr->type)) {
|
|
if (ptr->type->path) {
|
|
/* if type has a path to some ID, use it */
|
|
ptrpath = ptr->type->path(ptr);
|
|
}
|
|
else if (ptr->type->nested && RNA_struct_is_ID(ptr->type->nested)) {
|
|
PointerRNA parentptr;
|
|
PropertyRNA *userprop;
|
|
|
|
/* find the property in the struct we're nested in that references this struct, and
|
|
* use its identifier as the first part of the path used...
|
|
*/
|
|
RNA_id_pointer_create(ptr->id.data, &parentptr);
|
|
userprop = RNA_struct_find_nested(&parentptr, ptr->type);
|
|
|
|
if (userprop)
|
|
ptrpath = BLI_strdup(RNA_property_identifier(userprop));
|
|
else
|
|
return NULL; /* can't do anything about this case yet... */
|
|
}
|
|
else if (RNA_struct_is_a(ptr->type, &RNA_PropertyGroup)) {
|
|
/* special case, easier to deal with here then in ptr->type->path() */
|
|
return rna_path_from_ID_to_idpgroup(ptr);
|
|
}
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
return ptrpath;
|
|
}
|
|
|
|
static void rna_path_array_multi_from_flat_index(
|
|
const int dimsize[RNA_MAX_ARRAY_LENGTH], const int totdims,
|
|
const int index_dim, int index,
|
|
int r_index_multi[RNA_MAX_ARRAY_LENGTH])
|
|
{
|
|
int dimsize_step[RNA_MAX_ARRAY_LENGTH + 1];
|
|
int i = totdims - 1;
|
|
dimsize_step[i + 1] = 1;
|
|
dimsize_step[i] = dimsize[i];
|
|
while (--i != -1) {
|
|
dimsize_step[i] = dimsize[i] * dimsize_step[i + 1];
|
|
}
|
|
while (++i != index_dim) {
|
|
int index_round = index / dimsize_step[i + 1];
|
|
r_index_multi[i] = index_round;
|
|
index -= (index_round * dimsize_step[i + 1]);
|
|
}
|
|
BLI_assert(index == 0);
|
|
}
|
|
|
|
static void rna_path_array_multi_string_from_flat_index(
|
|
PointerRNA *ptr, PropertyRNA *prop, int index_dim, int index,
|
|
char *index_str, int index_str_len)
|
|
{
|
|
int dimsize[RNA_MAX_ARRAY_LENGTH];
|
|
int totdims = RNA_property_array_dimension(ptr, prop, dimsize);
|
|
int index_multi[RNA_MAX_ARRAY_LENGTH];
|
|
|
|
rna_path_array_multi_from_flat_index(dimsize, totdims, index_dim, index, index_multi);
|
|
|
|
for (int i = 0, offset = 0; (i < index_dim) && (offset < index_str_len); i++) {
|
|
offset += BLI_snprintf_rlen(&index_str[offset], index_str_len - offset, "[%d]", index_multi[i]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* \param index_dim: The dimension to show, 0 disables. 1 for 1d array, 2 for 2d. etc.
|
|
* \param index: The *flattened* index to use when \a ``index_dim > 0``,
|
|
* this is expanded when used with multi-dimensional arrays.
|
|
*/
|
|
char *RNA_path_from_ID_to_property_index(PointerRNA *ptr, PropertyRNA *prop, int index_dim, int index)
|
|
{
|
|
const bool is_rna = (prop->magic == RNA_MAGIC);
|
|
const char *propname;
|
|
char *ptrpath, *path;
|
|
|
|
if (!ptr->id.data || !ptr->data)
|
|
return NULL;
|
|
|
|
/* path from ID to the struct holding this property */
|
|
ptrpath = RNA_path_from_ID_to_struct(ptr);
|
|
|
|
propname = RNA_property_identifier(prop);
|
|
|
|
/* support indexing w/ multi-dimensional arrays */
|
|
char index_str[RNA_MAX_ARRAY_LENGTH * 12 + 1];
|
|
if (index_dim == 0) {
|
|
index_str[0] = '\0';
|
|
}
|
|
else {
|
|
rna_path_array_multi_string_from_flat_index(
|
|
ptr, prop, index_dim, index,
|
|
index_str, sizeof(index_str));
|
|
}
|
|
|
|
if (ptrpath) {
|
|
if (is_rna) {
|
|
path = BLI_sprintfN("%s.%s%s", ptrpath, propname, index_str);
|
|
}
|
|
else {
|
|
char propname_esc[MAX_IDPROP_NAME * 2];
|
|
BLI_strescape(propname_esc, propname, sizeof(propname_esc));
|
|
path = BLI_sprintfN("%s[\"%s\"]%s", ptrpath, propname_esc, index_str);
|
|
}
|
|
MEM_freeN(ptrpath);
|
|
}
|
|
else if (RNA_struct_is_ID(ptr->type)) {
|
|
if (is_rna) {
|
|
path = BLI_sprintfN("%s%s", propname, index_str);
|
|
}
|
|
else {
|
|
char propname_esc[MAX_IDPROP_NAME * 2];
|
|
BLI_strescape(propname_esc, propname, sizeof(propname_esc));
|
|
path = BLI_sprintfN("[\"%s\"]%s", propname_esc, index_str);
|
|
}
|
|
}
|
|
else {
|
|
path = NULL;
|
|
}
|
|
|
|
return path;
|
|
}
|
|
|
|
char *RNA_path_from_ID_to_property(PointerRNA *ptr, PropertyRNA *prop)
|
|
{
|
|
return RNA_path_from_ID_to_property_index(ptr, prop, 0, -1);
|
|
}
|
|
|
|
/**
|
|
* \return the path to given ptr/prop from the closest ancestor of given type, if any (else return NULL).
|
|
*/
|
|
char *RNA_path_resolve_from_type_to_property(
|
|
PointerRNA *ptr, PropertyRNA *prop,
|
|
const StructRNA *type)
|
|
{
|
|
/* Try to recursively find an "type"'d ancestor,
|
|
* to handle situations where path from ID is not enough. */
|
|
PointerRNA idptr;
|
|
ListBase path_elems = {NULL};
|
|
char *path = NULL;
|
|
char *full_path = RNA_path_from_ID_to_property(ptr, prop);
|
|
|
|
if (full_path == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
RNA_id_pointer_create(ptr->id.data, &idptr);
|
|
|
|
if (RNA_path_resolve_elements(&idptr, full_path, &path_elems)) {
|
|
PropertyElemRNA *prop_elem;
|
|
|
|
for (prop_elem = path_elems.last; prop_elem; prop_elem = prop_elem->prev) {
|
|
if (RNA_struct_is_a(prop_elem->ptr.type, type)) {
|
|
char *ref_path = RNA_path_from_ID_to_struct(&prop_elem->ptr);
|
|
if (ref_path) {
|
|
path = BLI_strdup(full_path + strlen(ref_path) + 1); /* +1 for the linking '.' */
|
|
MEM_freeN(ref_path);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
BLI_freelistN(&path_elems);
|
|
}
|
|
|
|
MEM_freeN(full_path);
|
|
return path;
|
|
}
|
|
|
|
/**
|
|
* Get the ID as a python representation, eg:
|
|
* bpy.data.foo["bar"]
|
|
*/
|
|
char *RNA_path_full_ID_py(ID *id)
|
|
{
|
|
char id_esc[(sizeof(id->name) - 2) * 2];
|
|
|
|
BLI_strescape(id_esc, id->name + 2, sizeof(id_esc));
|
|
|
|
return BLI_sprintfN("bpy.data.%s[\"%s\"]", BKE_idcode_to_name_plural(GS(id->name)), id_esc);
|
|
}
|
|
|
|
/**
|
|
* Get the ID.struct as a python representation, eg:
|
|
* bpy.data.foo["bar"].some_struct
|
|
*/
|
|
char *RNA_path_full_struct_py(struct PointerRNA *ptr)
|
|
{
|
|
char *id_path;
|
|
char *data_path;
|
|
|
|
char *ret;
|
|
|
|
if (!ptr->id.data) {
|
|
return NULL;
|
|
}
|
|
|
|
/* never fails */
|
|
id_path = RNA_path_full_ID_py(ptr->id.data);
|
|
|
|
data_path = RNA_path_from_ID_to_struct(ptr);
|
|
|
|
/* XXX data_path may be NULL (see #36788), do we want to get the 'bpy.data.foo["bar"].(null)' stuff? */
|
|
ret = BLI_sprintfN("%s.%s", id_path, data_path);
|
|
|
|
if (data_path) {
|
|
MEM_freeN(data_path);
|
|
}
|
|
MEM_freeN(id_path);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Get the ID.struct.property as a python representation, eg:
|
|
* bpy.data.foo["bar"].some_struct.some_prop[10]
|
|
*/
|
|
char *RNA_path_full_property_py_ex(PointerRNA *ptr, PropertyRNA *prop, int index, bool use_fallback)
|
|
{
|
|
char *id_path;
|
|
const char *data_delim;
|
|
const char *data_path;
|
|
bool data_path_free;
|
|
|
|
char *ret;
|
|
|
|
if (!ptr->id.data) {
|
|
return NULL;
|
|
}
|
|
|
|
/* never fails */
|
|
id_path = RNA_path_full_ID_py(ptr->id.data);
|
|
|
|
data_path = RNA_path_from_ID_to_property(ptr, prop);
|
|
if (data_path) {
|
|
data_delim = (data_path[0] == '[') ? "" : ".";
|
|
data_path_free = true;
|
|
}
|
|
else {
|
|
if (use_fallback) {
|
|
/* fuzzy fallback. be explicit in our ignoranc. */
|
|
data_path = RNA_property_identifier(prop);
|
|
data_delim = " ... ";
|
|
}
|
|
else {
|
|
data_delim = ".";
|
|
|
|
}
|
|
data_path_free = false;
|
|
}
|
|
|
|
|
|
if ((index == -1) || (RNA_property_array_check(prop) == false)) {
|
|
ret = BLI_sprintfN("%s%s%s",
|
|
id_path, data_delim, data_path);
|
|
}
|
|
else {
|
|
ret = BLI_sprintfN("%s%s%s[%d]",
|
|
id_path, data_delim, data_path, index);
|
|
}
|
|
MEM_freeN(id_path);
|
|
if (data_path_free) {
|
|
MEM_freeN((void *)data_path);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
char *RNA_path_full_property_py(PointerRNA *ptr, PropertyRNA *prop, int index)
|
|
{
|
|
return RNA_path_full_property_py_ex(ptr, prop, index, false);
|
|
}
|
|
|
|
/**
|
|
* Get the struct.property as a python representation, eg:
|
|
* some_struct.some_prop[10]
|
|
*/
|
|
char *RNA_path_struct_property_py(PointerRNA *ptr, PropertyRNA *prop, int index)
|
|
{
|
|
char *data_path;
|
|
|
|
char *ret;
|
|
|
|
if (!ptr->id.data) {
|
|
return NULL;
|
|
}
|
|
|
|
data_path = RNA_path_from_ID_to_property(ptr, prop);
|
|
|
|
if (data_path == NULL) {
|
|
/* this may not be an ID at all, check for simple when pointer owns property.
|
|
* TODO, more complex nested case */
|
|
if (!RNA_struct_is_ID(ptr->type)) {
|
|
const char *prop_identifier = RNA_property_identifier(prop);
|
|
if (RNA_struct_find_property(ptr, prop_identifier) == prop) {
|
|
data_path = BLI_strdup(prop_identifier);
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((index == -1) || (RNA_property_array_check(prop) == false)) {
|
|
ret = BLI_sprintfN("%s",
|
|
data_path);
|
|
}
|
|
else {
|
|
ret = BLI_sprintfN("%s[%d]",
|
|
data_path, index);
|
|
}
|
|
|
|
if (data_path) {
|
|
MEM_freeN(data_path);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Get the struct.property as a python representation, eg:
|
|
* some_prop[10]
|
|
*/
|
|
char *RNA_path_property_py(PointerRNA *UNUSED(ptr), PropertyRNA *prop, int index)
|
|
{
|
|
char *ret;
|
|
|
|
if ((index == -1) || (RNA_property_array_check(prop) == false)) {
|
|
ret = BLI_sprintfN("%s",
|
|
RNA_property_identifier(prop));
|
|
}
|
|
else {
|
|
ret = BLI_sprintfN("%s[%d]",
|
|
RNA_property_identifier(prop), index);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Quick name based property access */
|
|
|
|
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
|
|
{
|
|
PropertyRNA *prop = RNA_struct_find_property(ptr, name);
|
|
|
|
if (prop) {
|
|
return RNA_property_boolean_get(ptr, prop);
|
|
}
|
|
else {
|
|
printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
void RNA_boolean_set(PointerRNA *ptr, const char *name, bool value)
|
|
{
|
|
PropertyRNA *prop = RNA_struct_find_property(ptr, name);
|
|
|
|
if (prop)
|
|
RNA_property_boolean_set(ptr, prop, value);
|
|
else
|
|
printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name);
|
|
}
|
|
|
|
void RNA_boolean_get_array(PointerRNA *ptr, const char *name, bool *values)
|
|
{
|
|
PropertyRNA *prop = RNA_struct_find_property(ptr, name);
|
|
|
|
if (prop)
|
|
RNA_property_boolean_get_array(ptr, prop, values);
|
|
else
|
|
printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name);
|
|
}
|
|
|
|
void RNA_boolean_set_array(PointerRNA *ptr, const char *name, const bool *values)
|
|
{
|
|
PropertyRNA *prop = RNA_struct_find_property(ptr, name);
|
|
|
|
if (prop)
|
|
RNA_property_boolean_set_array(ptr, prop, values);
|
|
else
|
|
printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name);
|
|
}
|
|
|
|
int RNA_int_get(PointerRNA *ptr, const char *name)
|
|
{
|
|
PropertyRNA *prop = RNA_struct_find_property(ptr, name);
|
|
|
|
if (prop) {
|
|
return RNA_property_int_get(ptr, prop);
|
|
}
|
|
else {
|
|
printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
void RNA_int_set(PointerRNA *ptr, const char *name, int value)
|
|
{
|
|
PropertyRNA *prop = RNA_struct_find_property(ptr, name);
|
|
|
|
if (prop)
|
|
RNA_property_int_set(ptr, prop, value);
|
|
else
|
|
printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name);
|
|
}
|
|
|
|
void RNA_int_get_array(PointerRNA *ptr, const char *name, int *values)
|
|
{
|
|
PropertyRNA *prop = RNA_struct_find_property(ptr, name);
|
|
|
|
if (prop)
|
|
RNA_property_int_get_array(ptr, prop, values);
|
|
else
|
|
printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name);
|
|
}
|
|
|
|
void RNA_int_set_array(PointerRNA *ptr, const char *name, const int *values)
|
|
{
|
|
PropertyRNA *prop = RNA_struct_find_property(ptr, name);
|
|
|
|
if (prop)
|
|
RNA_property_int_set_array(ptr, prop, values);
|
|
else
|
|
printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name);
|
|
}
|
|
|
|
float RNA_float_get(PointerRNA *ptr, const char *name)
|
|
{
|
|
PropertyRNA *prop = RNA_struct_find_property(ptr, name);
|
|
|
|
if (prop) {
|
|
return RNA_property_float_get(ptr, prop);
|
|
}
|
|
else {
|
|
printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
void RNA_float_set(PointerRNA *ptr, const char *name, float value)
|
|
{
|
|
PropertyRNA *prop = RNA_struct_find_property(ptr, name);
|
|
|
|
if (prop)
|
|
RNA_property_float_set(ptr, prop, value);
|
|
else
|
|
printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name);
|
|
}
|
|
|
|
void RNA_float_get_array(PointerRNA *ptr, const char *name, float *values)
|
|
{
|
|
PropertyRNA *prop = RNA_struct_find_property(ptr, name);
|
|
|
|
if (prop)
|
|
RNA_property_float_get_array(ptr, prop, values);
|
|
else
|
|
printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name);
|
|
}
|
|
|
|
void RNA_float_set_array(PointerRNA *ptr, const char *name, const float *values)
|
|
{
|
|
PropertyRNA *prop = RNA_struct_find_property(ptr, name);
|
|
|
|
if (prop)
|
|
RNA_property_float_set_array(ptr, prop, values);
|
|
else
|
|
printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name);
|
|
}
|
|
|
|
int RNA_enum_get(PointerRNA *ptr, const char *name)
|
|
{
|
|
PropertyRNA *prop = RNA_struct_find_property(ptr, name);
|
|
|
|
if (prop) {
|
|
return RNA_property_enum_get(ptr, prop);
|
|
}
|
|
else {
|
|
printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
void RNA_enum_set(PointerRNA *ptr, const char *name, int value)
|
|
{
|
|
PropertyRNA *prop = RNA_struct_find_property(ptr, name);
|
|
|
|
if (prop)
|
|
RNA_property_enum_set(ptr, prop, value);
|
|
else
|
|
printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name);
|
|
}
|
|
|
|
void RNA_enum_set_identifier(bContext *C, PointerRNA *ptr, const char *name, const char *id)
|
|
{
|
|
PropertyRNA *prop = RNA_struct_find_property(ptr, name);
|
|
|
|
if (prop) {
|
|
int value;
|
|
if (RNA_property_enum_value(C, ptr, prop, id, &value))
|
|
RNA_property_enum_set(ptr, prop, value);
|
|
else
|
|
printf("%s: %s.%s has no enum id '%s'.\n", __func__, ptr->type->identifier, name, id);
|
|
}
|
|
else {
|
|
printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name);
|
|
}
|
|
}
|
|
|
|
bool RNA_enum_is_equal(bContext *C, PointerRNA *ptr, const char *name, const char *enumname)
|
|
{
|
|
PropertyRNA *prop = RNA_struct_find_property(ptr, name);
|
|
const EnumPropertyItem *item;
|
|
bool free;
|
|
|
|
if (prop) {
|
|
int i;
|
|
bool cmp = false;
|
|
|
|
RNA_property_enum_items(C, ptr, prop, &item, NULL, &free);
|
|
i = RNA_enum_from_identifier(item, enumname);
|
|
if (i != -1) {
|
|
cmp = (item[i].value == RNA_property_enum_get(ptr, prop));
|
|
}
|
|
|
|
if (free) {
|
|
MEM_freeN((void *)item);
|
|
}
|
|
|
|
if (i != -1) {
|
|
return cmp;
|
|
}
|
|
|
|
printf("%s: %s.%s item %s not found.\n", __func__, ptr->type->identifier, name, enumname);
|
|
return false;
|
|
}
|
|
else {
|
|
printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool RNA_enum_value_from_id(const EnumPropertyItem *item, const char *identifier, int *r_value)
|
|
{
|
|
const int i = RNA_enum_from_identifier(item, identifier);
|
|
if (i != -1) {
|
|
*r_value = item[i].value;
|
|
return true;
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool RNA_enum_id_from_value(const EnumPropertyItem *item, int value, const char **r_identifier)
|
|
{
|
|
const int i = RNA_enum_from_value(item, value);
|
|
if (i != -1) {
|
|
*r_identifier = item[i].identifier;
|
|
return true;
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool RNA_enum_icon_from_value(const EnumPropertyItem *item, int value, int *r_icon)
|
|
{
|
|
const int i = RNA_enum_from_value(item, value);
|
|
if (i != -1) {
|
|
*r_icon = item[i].icon;
|
|
return true;
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool RNA_enum_name_from_value(const EnumPropertyItem *item, int value, const char **r_name)
|
|
{
|
|
const int i = RNA_enum_from_value(item, value);
|
|
if (i != -1) {
|
|
*r_name = item[i].name;
|
|
return true;
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void RNA_string_get(PointerRNA *ptr, const char *name, char *value)
|
|
{
|
|
PropertyRNA *prop = RNA_struct_find_property(ptr, name);
|
|
|
|
if (prop) {
|
|
RNA_property_string_get(ptr, prop, value);
|
|
}
|
|
else {
|
|
printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name);
|
|
value[0] = '\0';
|
|
}
|
|
}
|
|
|
|
char *RNA_string_get_alloc(PointerRNA *ptr, const char *name, char *fixedbuf, int fixedlen)
|
|
{
|
|
PropertyRNA *prop = RNA_struct_find_property(ptr, name);
|
|
|
|
if (prop) {
|
|
return RNA_property_string_get_alloc(ptr, prop, fixedbuf, fixedlen, NULL); /* TODO, pass length */
|
|
}
|
|
else {
|
|
printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
int RNA_string_length(PointerRNA *ptr, const char *name)
|
|
{
|
|
PropertyRNA *prop = RNA_struct_find_property(ptr, name);
|
|
|
|
if (prop) {
|
|
return RNA_property_string_length(ptr, prop);
|
|
}
|
|
else {
|
|
printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
void RNA_string_set(PointerRNA *ptr, const char *name, const char *value)
|
|
{
|
|
PropertyRNA *prop = RNA_struct_find_property(ptr, name);
|
|
|
|
if (prop)
|
|
RNA_property_string_set(ptr, prop, value);
|
|
else
|
|
printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name);
|
|
}
|
|
|
|
PointerRNA RNA_pointer_get(PointerRNA *ptr, const char *name)
|
|
{
|
|
PropertyRNA *prop = RNA_struct_find_property(ptr, name);
|
|
|
|
if (prop) {
|
|
return RNA_property_pointer_get(ptr, prop);
|
|
}
|
|
else {
|
|
printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name);
|
|
|
|
return PointerRNA_NULL;
|
|
}
|
|
}
|
|
|
|
void RNA_pointer_set(PointerRNA *ptr, const char *name, PointerRNA ptr_value)
|
|
{
|
|
PropertyRNA *prop = RNA_struct_find_property(ptr, name);
|
|
|
|
if (prop) {
|
|
RNA_property_pointer_set(ptr, prop, ptr_value);
|
|
}
|
|
else {
|
|
printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name);
|
|
}
|
|
}
|
|
|
|
void RNA_pointer_add(PointerRNA *ptr, const char *name)
|
|
{
|
|
PropertyRNA *prop = RNA_struct_find_property(ptr, name);
|
|
|
|
if (prop)
|
|
RNA_property_pointer_add(ptr, prop);
|
|
else
|
|
printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name);
|
|
}
|
|
|
|
void RNA_collection_begin(PointerRNA *ptr, const char *name, CollectionPropertyIterator *iter)
|
|
{
|
|
PropertyRNA *prop = RNA_struct_find_property(ptr, name);
|
|
|
|
if (prop)
|
|
RNA_property_collection_begin(ptr, prop, iter);
|
|
else
|
|
printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name);
|
|
}
|
|
|
|
void RNA_collection_add(PointerRNA *ptr, const char *name, PointerRNA *r_value)
|
|
{
|
|
PropertyRNA *prop = RNA_struct_find_property(ptr, name);
|
|
|
|
if (prop)
|
|
RNA_property_collection_add(ptr, prop, r_value);
|
|
else
|
|
printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name);
|
|
}
|
|
|
|
void RNA_collection_clear(PointerRNA *ptr, const char *name)
|
|
{
|
|
PropertyRNA *prop = RNA_struct_find_property(ptr, name);
|
|
|
|
if (prop)
|
|
RNA_property_collection_clear(ptr, prop);
|
|
else
|
|
printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name);
|
|
}
|
|
|
|
int RNA_collection_length(PointerRNA *ptr, const char *name)
|
|
{
|
|
PropertyRNA *prop = RNA_struct_find_property(ptr, name);
|
|
|
|
if (prop) {
|
|
return RNA_property_collection_length(ptr, prop);
|
|
}
|
|
else {
|
|
printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
bool RNA_property_is_set_ex(PointerRNA *ptr, PropertyRNA *prop, bool use_ghost)
|
|
{
|
|
prop = rna_ensure_property(prop);
|
|
if (prop->flag & PROP_IDPROPERTY) {
|
|
IDProperty *idprop = rna_idproperty_find(ptr, prop->identifier);
|
|
return ((idprop != NULL) && (use_ghost == false || !(idprop->flag & IDP_FLAG_GHOST)));
|
|
}
|
|
else {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
bool RNA_property_is_set(PointerRNA *ptr, PropertyRNA *prop)
|
|
{
|
|
prop = rna_ensure_property(prop);
|
|
if (prop->flag & PROP_IDPROPERTY) {
|
|
IDProperty *idprop = rna_idproperty_find(ptr, prop->identifier);
|
|
return ((idprop != NULL) && !(idprop->flag & IDP_FLAG_GHOST));
|
|
}
|
|
else {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
void RNA_property_unset(PointerRNA *ptr, PropertyRNA *prop)
|
|
{
|
|
prop = rna_ensure_property(prop);
|
|
if (prop->flag & PROP_IDPROPERTY) {
|
|
rna_idproperty_free(ptr, prop->identifier);
|
|
}
|
|
}
|
|
|
|
bool RNA_struct_property_is_set_ex(PointerRNA *ptr, const char *identifier, bool use_ghost)
|
|
{
|
|
PropertyRNA *prop = RNA_struct_find_property(ptr, identifier);
|
|
|
|
if (prop) {
|
|
return RNA_property_is_set_ex(ptr, prop, use_ghost);
|
|
}
|
|
else {
|
|
/* python raises an error */
|
|
/* printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name); */
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
bool RNA_struct_property_is_set(PointerRNA *ptr, const char *identifier)
|
|
{
|
|
PropertyRNA *prop = RNA_struct_find_property(ptr, identifier);
|
|
|
|
if (prop) {
|
|
return RNA_property_is_set(ptr, prop);
|
|
}
|
|
else {
|
|
/* python raises an error */
|
|
/* printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name); */
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
void RNA_struct_property_unset(PointerRNA *ptr, const char *identifier)
|
|
{
|
|
PropertyRNA *prop = RNA_struct_find_property(ptr, identifier);
|
|
|
|
if (prop) {
|
|
RNA_property_unset(ptr, prop);
|
|
}
|
|
}
|
|
|
|
bool RNA_property_is_idprop(const PropertyRNA *prop)
|
|
{
|
|
return (prop->magic != RNA_MAGIC);
|
|
}
|
|
|
|
/* mainly for the UI */
|
|
bool RNA_property_is_unlink(PropertyRNA *prop)
|
|
{
|
|
const int flag = RNA_property_flag(prop);
|
|
if (RNA_property_type(prop) == PROP_STRING) {
|
|
return (flag & PROP_NEVER_UNLINK) == 0;
|
|
}
|
|
else {
|
|
return (flag & (PROP_NEVER_UNLINK | PROP_NEVER_NULL)) == 0;
|
|
}
|
|
}
|
|
|
|
/* string representation of a property, python
|
|
* compatible but can be used for display too,
|
|
* context may be NULL */
|
|
char *RNA_pointer_as_string_id(bContext *C, PointerRNA *ptr)
|
|
{
|
|
DynStr *dynstr = BLI_dynstr_new();
|
|
char *cstring;
|
|
|
|
const char *propname;
|
|
int first_time = 1;
|
|
|
|
BLI_dynstr_append(dynstr, "{");
|
|
|
|
RNA_STRUCT_BEGIN (ptr, prop)
|
|
{
|
|
propname = RNA_property_identifier(prop);
|
|
|
|
if (STREQ(propname, "rna_type"))
|
|
continue;
|
|
|
|
if (first_time == 0)
|
|
BLI_dynstr_append(dynstr, ", ");
|
|
first_time = 0;
|
|
|
|
cstring = RNA_property_as_string(C, ptr, prop, -1, INT_MAX);
|
|
BLI_dynstr_appendf(dynstr, "\"%s\":%s", propname, cstring);
|
|
MEM_freeN(cstring);
|
|
}
|
|
RNA_STRUCT_END;
|
|
|
|
BLI_dynstr_append(dynstr, "}");
|
|
|
|
|
|
cstring = BLI_dynstr_get_cstring(dynstr);
|
|
BLI_dynstr_free(dynstr);
|
|
return cstring;
|
|
}
|
|
|
|
static char *rna_pointer_as_string__bldata(PointerRNA *ptr)
|
|
{
|
|
if (ptr->type == NULL || ptr->id.data == NULL) {
|
|
return BLI_strdup("None");
|
|
}
|
|
else if (RNA_struct_is_ID(ptr->type)) {
|
|
return RNA_path_full_ID_py(ptr->id.data);
|
|
}
|
|
else {
|
|
return RNA_path_full_struct_py(ptr);
|
|
}
|
|
}
|
|
|
|
char *RNA_pointer_as_string(bContext *C, PointerRNA *ptr, PropertyRNA *prop_ptr, PointerRNA *ptr_prop)
|
|
{
|
|
IDProperty *prop;
|
|
if (ptr_prop->data == NULL) {
|
|
return BLI_strdup("None");
|
|
}
|
|
else if ((prop = rna_idproperty_check(&prop_ptr, ptr)) && prop->type != IDP_ID) {
|
|
return RNA_pointer_as_string_id(C, ptr_prop);
|
|
}
|
|
else {
|
|
return rna_pointer_as_string__bldata(ptr_prop);
|
|
}
|
|
}
|
|
|
|
/* context can be NULL */
|
|
char *RNA_pointer_as_string_keywords_ex(bContext *C, PointerRNA *ptr,
|
|
const bool as_function, const bool all_args, const bool nested_args,
|
|
const int max_prop_length,
|
|
PropertyRNA *iterprop)
|
|
{
|
|
const char *arg_name = NULL;
|
|
|
|
PropertyRNA *prop;
|
|
|
|
DynStr *dynstr = BLI_dynstr_new();
|
|
char *cstring, *buf;
|
|
bool first_iter = true;
|
|
int flag, flag_parameter;
|
|
|
|
RNA_PROP_BEGIN (ptr, propptr, iterprop)
|
|
{
|
|
prop = propptr.data;
|
|
|
|
flag = RNA_property_flag(prop);
|
|
flag_parameter = RNA_parameter_flag(prop);
|
|
|
|
if (as_function && (flag_parameter & PARM_OUTPUT)) {
|
|
continue;
|
|
}
|
|
|
|
arg_name = RNA_property_identifier(prop);
|
|
|
|
if (STREQ(arg_name, "rna_type")) {
|
|
continue;
|
|
}
|
|
|
|
if ((nested_args == false) && (RNA_property_type(prop) == PROP_POINTER)) {
|
|
continue;
|
|
}
|
|
|
|
if (as_function && (prop->flag_parameter & PARM_REQUIRED)) {
|
|
/* required args don't have useful defaults */
|
|
BLI_dynstr_appendf(dynstr, first_iter ? "%s" : ", %s", arg_name);
|
|
first_iter = false;
|
|
}
|
|
else {
|
|
bool ok = true;
|
|
|
|
if (all_args == true) {
|
|
/* pass */
|
|
}
|
|
else if (RNA_struct_idprops_check(ptr->type)) {
|
|
ok = RNA_property_is_set(ptr, prop);
|
|
}
|
|
|
|
if (ok) {
|
|
if (as_function && RNA_property_type(prop) == PROP_POINTER) {
|
|
/* don't expand pointers for functions */
|
|
if (flag & PROP_NEVER_NULL) {
|
|
/* we cant really do the right thing here. arg=arg?, hrmf! */
|
|
buf = BLI_strdup(arg_name);
|
|
}
|
|
else {
|
|
buf = BLI_strdup("None");
|
|
}
|
|
}
|
|
else {
|
|
buf = RNA_property_as_string(C, ptr, prop, -1, max_prop_length);
|
|
}
|
|
|
|
BLI_dynstr_appendf(dynstr, first_iter ? "%s=%s" : ", %s=%s", arg_name, buf);
|
|
first_iter = false;
|
|
MEM_freeN(buf);
|
|
}
|
|
}
|
|
}
|
|
RNA_PROP_END;
|
|
|
|
cstring = BLI_dynstr_get_cstring(dynstr);
|
|
BLI_dynstr_free(dynstr);
|
|
return cstring;
|
|
}
|
|
|
|
char *RNA_pointer_as_string_keywords(bContext *C, PointerRNA *ptr,
|
|
const bool as_function, const bool all_args, const bool nested_args,
|
|
const int max_prop_length)
|
|
{
|
|
PropertyRNA *iterprop;
|
|
|
|
iterprop = RNA_struct_iterator_property(ptr->type);
|
|
|
|
return RNA_pointer_as_string_keywords_ex(C, ptr, as_function, all_args, nested_args,
|
|
max_prop_length, iterprop);
|
|
}
|
|
|
|
char *RNA_function_as_string_keywords(bContext *C, FunctionRNA *func,
|
|
const bool as_function, const bool all_args,
|
|
const int max_prop_length)
|
|
{
|
|
PointerRNA funcptr;
|
|
PropertyRNA *iterprop;
|
|
|
|
RNA_pointer_create(NULL, &RNA_Function, func, &funcptr);
|
|
|
|
iterprop = RNA_struct_find_property(&funcptr, "parameters");
|
|
|
|
RNA_struct_iterator_property(funcptr.type);
|
|
|
|
return RNA_pointer_as_string_keywords_ex(C, &funcptr, as_function, all_args, true,
|
|
max_prop_length, iterprop);
|
|
}
|
|
|
|
static const char *bool_as_py_string(const int var)
|
|
{
|
|
return var ? "True" : "False";
|
|
}
|
|
|
|
static void *rna_array_as_string_alloc(
|
|
int type, int len, PointerRNA *ptr, PropertyRNA *prop, void **r_buf_end)
|
|
{
|
|
void *buf_ret = NULL;
|
|
if (type == PROP_BOOLEAN) {
|
|
bool *buf = buf_ret = MEM_mallocN(sizeof(*buf) * len, __func__);
|
|
RNA_property_boolean_get_array(ptr, prop, buf);
|
|
*r_buf_end = buf + len;
|
|
}
|
|
else if (type == PROP_INT) {
|
|
int *buf = buf_ret = MEM_mallocN(sizeof(*buf) * len, __func__);
|
|
RNA_property_int_get_array(ptr, prop, buf);
|
|
*r_buf_end = buf + len;
|
|
}
|
|
else if (type == PROP_FLOAT) {
|
|
float *buf = buf_ret = MEM_mallocN(sizeof(*buf) * len, __func__);
|
|
RNA_property_float_get_array(ptr, prop, buf);
|
|
*r_buf_end = buf + len;
|
|
}
|
|
else {
|
|
BLI_assert(0);
|
|
}
|
|
return buf_ret;
|
|
}
|
|
|
|
static void rna_array_as_string_elem(
|
|
int type, void **buf_p, int len, DynStr *dynstr)
|
|
{
|
|
/* This will print a comma seperated string of the array elements from
|
|
* buf start to len. We will add a comma if len == 1 to preserve tuples. */
|
|
const int end = len - 1;
|
|
if (type == PROP_BOOLEAN) {
|
|
bool *buf = *buf_p;
|
|
for (int i = 0; i < len; i++, buf++) {
|
|
BLI_dynstr_appendf(dynstr, (i < end || !end) ? "%s, " : "%s", bool_as_py_string(*buf));
|
|
}
|
|
*buf_p = buf;
|
|
}
|
|
else if (type == PROP_INT) {
|
|
int *buf = *buf_p;
|
|
for (int i = 0; i < len; i++, buf++) {
|
|
BLI_dynstr_appendf(dynstr, (i < end || !end) ? "%d, " : "%d", *buf);
|
|
}
|
|
*buf_p = buf;
|
|
}
|
|
else if (type == PROP_FLOAT) {
|
|
float *buf = *buf_p;
|
|
for (int i = 0; i < len; i++, buf++) {
|
|
BLI_dynstr_appendf(dynstr, (i < end || !end) ? "%g, " : "%g", *buf);
|
|
}
|
|
*buf_p = buf;
|
|
}
|
|
else {
|
|
BLI_assert(0);
|
|
}
|
|
}
|
|
|
|
static void rna_array_as_string_recursive(
|
|
int type, void **buf_p, int totdim, const int *dim_size, DynStr *dynstr)
|
|
{
|
|
BLI_dynstr_append(dynstr, "(");
|
|
if (totdim > 1) {
|
|
totdim--;
|
|
const int end = dim_size[totdim] - 1;
|
|
for (int i = 0; i <= end; i++) {
|
|
rna_array_as_string_recursive(type, buf_p, totdim, dim_size, dynstr);
|
|
if (i < end || !end) {
|
|
BLI_dynstr_append(dynstr, ", ");
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
rna_array_as_string_elem(type, buf_p, dim_size[0], dynstr);
|
|
}
|
|
BLI_dynstr_append(dynstr, ")");
|
|
}
|
|
|
|
static void rna_array_as_string(int type, int len, PointerRNA *ptr, PropertyRNA *prop, DynStr *dynstr)
|
|
{
|
|
void *buf_end;
|
|
void *buf = rna_array_as_string_alloc(type, len, ptr, prop, &buf_end);
|
|
void *buf_step = buf;
|
|
int totdim, dim_size[RNA_MAX_ARRAY_DIMENSION];
|
|
|
|
totdim = RNA_property_array_dimension(ptr, prop, dim_size);
|
|
|
|
rna_array_as_string_recursive(type, &buf_step, totdim, dim_size, dynstr);
|
|
BLI_assert(buf_step == buf_end);
|
|
MEM_freeN(buf);
|
|
}
|
|
|
|
char *RNA_property_as_string(bContext *C, PointerRNA *ptr, PropertyRNA *prop, int index, int max_prop_length)
|
|
{
|
|
int type = RNA_property_type(prop);
|
|
int len = RNA_property_array_length(ptr, prop);
|
|
|
|
DynStr *dynstr = BLI_dynstr_new();
|
|
char *cstring;
|
|
|
|
/* see if we can coerce into a python type - PropertyType */
|
|
switch (type) {
|
|
case PROP_BOOLEAN:
|
|
if (len == 0) {
|
|
BLI_dynstr_append(dynstr, bool_as_py_string(RNA_property_boolean_get(ptr, prop)));
|
|
}
|
|
else {
|
|
if (index != -1) {
|
|
BLI_dynstr_append(dynstr, bool_as_py_string(RNA_property_boolean_get_index(ptr, prop, index)));
|
|
}
|
|
else {
|
|
rna_array_as_string(type, len, ptr, prop, dynstr);
|
|
}
|
|
}
|
|
break;
|
|
case PROP_INT:
|
|
if (len == 0) {
|
|
BLI_dynstr_appendf(dynstr, "%d", RNA_property_int_get(ptr, prop));
|
|
}
|
|
else {
|
|
if (index != -1) {
|
|
BLI_dynstr_appendf(dynstr, "%d", RNA_property_int_get_index(ptr, prop, index));
|
|
}
|
|
else {
|
|
rna_array_as_string(type, len, ptr, prop, dynstr);
|
|
}
|
|
}
|
|
break;
|
|
case PROP_FLOAT:
|
|
if (len == 0) {
|
|
BLI_dynstr_appendf(dynstr, "%g", RNA_property_float_get(ptr, prop));
|
|
}
|
|
else {
|
|
if (index != -1) {
|
|
BLI_dynstr_appendf(dynstr, "%g", RNA_property_float_get_index(ptr, prop, index));
|
|
}
|
|
else {
|
|
rna_array_as_string(type, len, ptr, prop, dynstr);
|
|
}
|
|
}
|
|
break;
|
|
case PROP_STRING:
|
|
{
|
|
char *buf_esc;
|
|
char *buf;
|
|
int length;
|
|
|
|
length = RNA_property_string_length(ptr, prop);
|
|
buf = MEM_mallocN(sizeof(char) * (length + 1), "RNA_property_as_string");
|
|
buf_esc = MEM_mallocN(sizeof(char) * (length * 2 + 1), "RNA_property_as_string esc");
|
|
RNA_property_string_get(ptr, prop, buf);
|
|
BLI_strescape(buf_esc, buf, length * 2 + 1);
|
|
MEM_freeN(buf);
|
|
BLI_dynstr_appendf(dynstr, "\"%s\"", buf_esc);
|
|
MEM_freeN(buf_esc);
|
|
break;
|
|
}
|
|
case PROP_ENUM:
|
|
{
|
|
/* string arrays don't exist */
|
|
const char *identifier;
|
|
int val = RNA_property_enum_get(ptr, prop);
|
|
|
|
if (RNA_property_flag(prop) & PROP_ENUM_FLAG) {
|
|
/* represent as a python set */
|
|
if (val) {
|
|
const EnumPropertyItem *item_array;
|
|
bool free;
|
|
|
|
BLI_dynstr_append(dynstr, "{");
|
|
|
|
RNA_property_enum_items(C, ptr, prop, &item_array, NULL, &free);
|
|
if (item_array) {
|
|
const EnumPropertyItem *item = item_array;
|
|
bool is_first = true;
|
|
for (; item->identifier; item++) {
|
|
if (item->identifier[0] && item->value & val) {
|
|
BLI_dynstr_appendf(dynstr, is_first ? "'%s'" : ", '%s'", item->identifier);
|
|
is_first = false;
|
|
}
|
|
}
|
|
|
|
if (free) {
|
|
MEM_freeN((void *)item_array);
|
|
}
|
|
}
|
|
|
|
BLI_dynstr_append(dynstr, "}");
|
|
}
|
|
else {
|
|
/* annoying exception, don't confuse with dictionary syntax above: {} */
|
|
BLI_dynstr_append(dynstr, "set()");
|
|
}
|
|
}
|
|
else if (RNA_property_enum_identifier(C, ptr, prop, val, &identifier)) {
|
|
BLI_dynstr_appendf(dynstr, "'%s'", identifier);
|
|
}
|
|
else {
|
|
BLI_dynstr_append(dynstr, "'<UNKNOWN ENUM>'");
|
|
}
|
|
break;
|
|
}
|
|
case PROP_POINTER:
|
|
{
|
|
PointerRNA tptr = RNA_property_pointer_get(ptr, prop);
|
|
cstring = RNA_pointer_as_string(C, ptr, prop, &tptr);
|
|
BLI_dynstr_append(dynstr, cstring);
|
|
MEM_freeN(cstring);
|
|
break;
|
|
}
|
|
case PROP_COLLECTION:
|
|
{
|
|
int i = 0;
|
|
CollectionPropertyIterator collect_iter;
|
|
BLI_dynstr_append(dynstr, "[");
|
|
|
|
for (RNA_property_collection_begin(ptr, prop, &collect_iter);
|
|
(i < max_prop_length) && collect_iter.valid;
|
|
RNA_property_collection_next(&collect_iter), i++)
|
|
{
|
|
PointerRNA itemptr = collect_iter.ptr;
|
|
|
|
if (i != 0)
|
|
BLI_dynstr_append(dynstr, ", ");
|
|
|
|
/* now get every prop of the collection */
|
|
cstring = RNA_pointer_as_string(C, ptr, prop, &itemptr);
|
|
BLI_dynstr_append(dynstr, cstring);
|
|
MEM_freeN(cstring);
|
|
}
|
|
|
|
RNA_property_collection_end(&collect_iter);
|
|
BLI_dynstr_append(dynstr, "]");
|
|
break;
|
|
}
|
|
default:
|
|
BLI_dynstr_append(dynstr, "'<UNKNOWN TYPE>'"); /* TODO */
|
|
break;
|
|
}
|
|
|
|
cstring = BLI_dynstr_get_cstring(dynstr);
|
|
BLI_dynstr_free(dynstr);
|
|
return cstring;
|
|
}
|
|
|
|
/* Function */
|
|
|
|
const char *RNA_function_identifier(FunctionRNA *func)
|
|
{
|
|
return func->identifier;
|
|
}
|
|
|
|
const char *RNA_function_ui_description(FunctionRNA *func)
|
|
{
|
|
return TIP_(func->description);
|
|
}
|
|
|
|
const char *RNA_function_ui_description_raw(FunctionRNA *func)
|
|
{
|
|
return func->description;
|
|
}
|
|
|
|
int RNA_function_flag(FunctionRNA *func)
|
|
{
|
|
return func->flag;
|
|
}
|
|
|
|
int RNA_function_defined(FunctionRNA *func)
|
|
{
|
|
return func->call != NULL;
|
|
}
|
|
|
|
PropertyRNA *RNA_function_get_parameter(PointerRNA *UNUSED(ptr), FunctionRNA *func, int index)
|
|
{
|
|
return BLI_findlink(&func->cont.properties, index);
|
|
}
|
|
|
|
PropertyRNA *RNA_function_find_parameter(PointerRNA *UNUSED(ptr), FunctionRNA *func, const char *identifier)
|
|
{
|
|
PropertyRNA *parm;
|
|
|
|
parm = func->cont.properties.first;
|
|
for (; parm; parm = parm->next)
|
|
if (STREQ(RNA_property_identifier(parm), identifier))
|
|
break;
|
|
|
|
return parm;
|
|
}
|
|
|
|
const ListBase *RNA_function_defined_parameters(FunctionRNA *func)
|
|
{
|
|
return &func->cont.properties;
|
|
}
|
|
|
|
/* Utility */
|
|
|
|
int RNA_parameter_flag(PropertyRNA *prop)
|
|
{
|
|
return (int)rna_ensure_property(prop)->flag_parameter;
|
|
}
|
|
|
|
ParameterList *RNA_parameter_list_create(ParameterList *parms, PointerRNA *UNUSED(ptr), FunctionRNA *func)
|
|
{
|
|
PropertyRNA *parm;
|
|
void *data;
|
|
int alloc_size = 0, size;
|
|
|
|
parms->arg_count = 0;
|
|
parms->ret_count = 0;
|
|
|
|
/* allocate data */
|
|
for (parm = func->cont.properties.first; parm; parm = parm->next) {
|
|
alloc_size += rna_parameter_size(parm);
|
|
|
|
if (parm->flag_parameter & PARM_OUTPUT)
|
|
parms->ret_count++;
|
|
else
|
|
parms->arg_count++;
|
|
}
|
|
|
|
parms->data = MEM_callocN(alloc_size, "RNA_parameter_list_create");
|
|
parms->func = func;
|
|
parms->alloc_size = alloc_size;
|
|
|
|
/* set default values */
|
|
data = parms->data;
|
|
|
|
for (parm = func->cont.properties.first; parm; parm = parm->next) {
|
|
size = rna_parameter_size(parm);
|
|
|
|
/* set length to 0, these need to be set later, see bpy_array.c's py_to_array */
|
|
if (parm->flag & PROP_DYNAMIC) {
|
|
ParameterDynAlloc *data_alloc = data;
|
|
data_alloc->array_tot = 0;
|
|
data_alloc->array = NULL;
|
|
}
|
|
|
|
if (!(parm->flag_parameter & PARM_REQUIRED) && !(parm->flag & PROP_DYNAMIC)) {
|
|
switch (parm->type) {
|
|
case PROP_BOOLEAN:
|
|
if (parm->arraydimension) rna_property_boolean_get_default_array_values((BoolPropertyRNA *)parm, data);
|
|
else memcpy(data, &((BoolPropertyRNA *)parm)->defaultvalue, size);
|
|
break;
|
|
case PROP_INT:
|
|
if (parm->arraydimension) rna_property_int_get_default_array_values((IntPropertyRNA *)parm, data);
|
|
else memcpy(data, &((IntPropertyRNA *)parm)->defaultvalue, size);
|
|
break;
|
|
case PROP_FLOAT:
|
|
if (parm->arraydimension) rna_property_float_get_default_array_values((FloatPropertyRNA *)parm, data);
|
|
else memcpy(data, &((FloatPropertyRNA *)parm)->defaultvalue, size);
|
|
break;
|
|
case PROP_ENUM:
|
|
memcpy(data, &((EnumPropertyRNA *)parm)->defaultvalue, size);
|
|
break;
|
|
case PROP_STRING:
|
|
{
|
|
const char *defvalue = ((StringPropertyRNA *)parm)->defaultvalue;
|
|
if (defvalue && defvalue[0]) {
|
|
/* causes bug [#29988], possibly this is only correct for thick wrapped
|
|
* need to look further into it - campbell */
|
|
#if 0
|
|
BLI_strncpy(data, defvalue, size);
|
|
#else
|
|
memcpy(data, &defvalue, size);
|
|
#endif
|
|
}
|
|
break;
|
|
}
|
|
case PROP_POINTER:
|
|
case PROP_COLLECTION:
|
|
break;
|
|
}
|
|
}
|
|
|
|
data = ((char *)data) + rna_parameter_size(parm);
|
|
}
|
|
|
|
return parms;
|
|
}
|
|
|
|
void RNA_parameter_list_free(ParameterList *parms)
|
|
{
|
|
PropertyRNA *parm;
|
|
int tot;
|
|
|
|
parm = parms->func->cont.properties.first;
|
|
for (tot = 0; parm; parm = parm->next) {
|
|
if (parm->type == PROP_COLLECTION)
|
|
BLI_freelistN((ListBase *)((char *)parms->data + tot));
|
|
else if (parm->flag & PROP_DYNAMIC) {
|
|
/* for dynamic arrays and strings, data is a pointer to an array */
|
|
ParameterDynAlloc *data_alloc = (void *)(((char *)parms->data) + tot);
|
|
if (data_alloc->array)
|
|
MEM_freeN(data_alloc->array);
|
|
}
|
|
|
|
tot += rna_parameter_size(parm);
|
|
}
|
|
|
|
MEM_freeN(parms->data);
|
|
parms->data = NULL;
|
|
|
|
parms->func = NULL;
|
|
}
|
|
|
|
int RNA_parameter_list_size(ParameterList *parms)
|
|
{
|
|
return parms->alloc_size;
|
|
}
|
|
|
|
int RNA_parameter_list_arg_count(ParameterList *parms)
|
|
{
|
|
return parms->arg_count;
|
|
}
|
|
|
|
int RNA_parameter_list_ret_count(ParameterList *parms)
|
|
{
|
|
return parms->ret_count;
|
|
}
|
|
|
|
void RNA_parameter_list_begin(ParameterList *parms, ParameterIterator *iter)
|
|
{
|
|
/* may be useful but unused now */
|
|
/* RNA_pointer_create(NULL, &RNA_Function, parms->func, &iter->funcptr); */ /*UNUSED*/
|
|
|
|
iter->parms = parms;
|
|
iter->parm = parms->func->cont.properties.first;
|
|
iter->valid = iter->parm != NULL;
|
|
iter->offset = 0;
|
|
|
|
if (iter->valid) {
|
|
iter->size = rna_parameter_size(iter->parm);
|
|
iter->data = (((char *)iter->parms->data)); /* +iter->offset, always 0 */
|
|
}
|
|
}
|
|
|
|
void RNA_parameter_list_next(ParameterIterator *iter)
|
|
{
|
|
iter->offset += iter->size;
|
|
iter->parm = iter->parm->next;
|
|
iter->valid = iter->parm != NULL;
|
|
|
|
if (iter->valid) {
|
|
iter->size = rna_parameter_size(iter->parm);
|
|
iter->data = (((char *)iter->parms->data) + iter->offset);
|
|
}
|
|
}
|
|
|
|
void RNA_parameter_list_end(ParameterIterator *UNUSED(iter))
|
|
{
|
|
/* nothing to do */
|
|
}
|
|
|
|
void RNA_parameter_get(ParameterList *parms, PropertyRNA *parm, void **value)
|
|
{
|
|
ParameterIterator iter;
|
|
|
|
RNA_parameter_list_begin(parms, &iter);
|
|
|
|
for (; iter.valid; RNA_parameter_list_next(&iter))
|
|
if (iter.parm == parm)
|
|
break;
|
|
|
|
if (iter.valid) {
|
|
if (parm->flag & PROP_DYNAMIC) {
|
|
/* for dynamic arrays and strings, data is a pointer to an array */
|
|
ParameterDynAlloc *data_alloc = iter.data;
|
|
*value = data_alloc->array;
|
|
}
|
|
else {
|
|
*value = iter.data;
|
|
}
|
|
}
|
|
else {
|
|
*value = NULL;
|
|
}
|
|
|
|
RNA_parameter_list_end(&iter);
|
|
}
|
|
|
|
void RNA_parameter_get_lookup(ParameterList *parms, const char *identifier, void **value)
|
|
{
|
|
PropertyRNA *parm;
|
|
|
|
parm = parms->func->cont.properties.first;
|
|
for (; parm; parm = parm->next)
|
|
if (STREQ(RNA_property_identifier(parm), identifier))
|
|
break;
|
|
|
|
if (parm)
|
|
RNA_parameter_get(parms, parm, value);
|
|
}
|
|
|
|
void RNA_parameter_set(ParameterList *parms, PropertyRNA *parm, const void *value)
|
|
{
|
|
ParameterIterator iter;
|
|
|
|
RNA_parameter_list_begin(parms, &iter);
|
|
|
|
for (; iter.valid; RNA_parameter_list_next(&iter))
|
|
if (iter.parm == parm)
|
|
break;
|
|
|
|
if (iter.valid) {
|
|
if (parm->flag & PROP_DYNAMIC) {
|
|
/* for dynamic arrays and strings, data is a pointer to an array */
|
|
ParameterDynAlloc *data_alloc = iter.data;
|
|
size_t size = 0;
|
|
switch (parm->type) {
|
|
case PROP_STRING:
|
|
size = sizeof(char);
|
|
break;
|
|
case PROP_INT:
|
|
case PROP_BOOLEAN:
|
|
size = sizeof(int);
|
|
break;
|
|
case PROP_FLOAT:
|
|
size = sizeof(float);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
size *= data_alloc->array_tot;
|
|
if (data_alloc->array)
|
|
MEM_freeN(data_alloc->array);
|
|
data_alloc->array = MEM_mallocN(size, __func__);
|
|
memcpy(data_alloc->array, value, size);
|
|
}
|
|
else {
|
|
memcpy(iter.data, value, iter.size);
|
|
}
|
|
}
|
|
|
|
RNA_parameter_list_end(&iter);
|
|
}
|
|
|
|
void RNA_parameter_set_lookup(ParameterList *parms, const char *identifier, const void *value)
|
|
{
|
|
PropertyRNA *parm;
|
|
|
|
parm = parms->func->cont.properties.first;
|
|
for (; parm; parm = parm->next)
|
|
if (STREQ(RNA_property_identifier(parm), identifier))
|
|
break;
|
|
|
|
if (parm)
|
|
RNA_parameter_set(parms, parm, value);
|
|
}
|
|
|
|
int RNA_parameter_dynamic_length_get(ParameterList *parms, PropertyRNA *parm)
|
|
{
|
|
ParameterIterator iter;
|
|
int len = 0;
|
|
|
|
RNA_parameter_list_begin(parms, &iter);
|
|
|
|
for (; iter.valid; RNA_parameter_list_next(&iter))
|
|
if (iter.parm == parm)
|
|
break;
|
|
|
|
if (iter.valid)
|
|
len = RNA_parameter_dynamic_length_get_data(parms, parm, iter.data);
|
|
|
|
RNA_parameter_list_end(&iter);
|
|
|
|
return len;
|
|
}
|
|
|
|
void RNA_parameter_dynamic_length_set(ParameterList *parms, PropertyRNA *parm, int length)
|
|
{
|
|
ParameterIterator iter;
|
|
|
|
RNA_parameter_list_begin(parms, &iter);
|
|
|
|
for (; iter.valid; RNA_parameter_list_next(&iter))
|
|
if (iter.parm == parm)
|
|
break;
|
|
|
|
if (iter.valid)
|
|
RNA_parameter_dynamic_length_set_data(parms, parm, iter.data, length);
|
|
|
|
RNA_parameter_list_end(&iter);
|
|
}
|
|
|
|
int RNA_parameter_dynamic_length_get_data(ParameterList *UNUSED(parms), PropertyRNA *parm, void *data)
|
|
{
|
|
if (parm->flag & PROP_DYNAMIC) {
|
|
return (int)((ParameterDynAlloc *)data)->array_tot;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void RNA_parameter_dynamic_length_set_data(ParameterList *UNUSED(parms), PropertyRNA *parm, void *data, int length)
|
|
{
|
|
if (parm->flag & PROP_DYNAMIC) {
|
|
((ParameterDynAlloc *)data)->array_tot = (intptr_t)length;
|
|
}
|
|
}
|
|
|
|
int RNA_function_call(bContext *C, ReportList *reports, PointerRNA *ptr, FunctionRNA *func, ParameterList *parms)
|
|
{
|
|
if (func->call) {
|
|
func->call(C, reports, ptr, parms);
|
|
|
|
return 0;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
int RNA_function_call_lookup(bContext *C, ReportList *reports, PointerRNA *ptr, const char *identifier,
|
|
ParameterList *parms)
|
|
{
|
|
FunctionRNA *func;
|
|
|
|
func = RNA_struct_find_function(ptr->type, identifier);
|
|
|
|
if (func)
|
|
return RNA_function_call(C, reports, ptr, func, parms);
|
|
|
|
return -1;
|
|
}
|
|
|
|
int RNA_function_call_direct(bContext *C, ReportList *reports, PointerRNA *ptr, FunctionRNA *func,
|
|
const char *format, ...)
|
|
{
|
|
va_list args;
|
|
int ret;
|
|
|
|
va_start(args, format);
|
|
|
|
ret = RNA_function_call_direct_va(C, reports, ptr, func, format, args);
|
|
|
|
va_end(args);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int RNA_function_call_direct_lookup(bContext *C, ReportList *reports, PointerRNA *ptr, const char *identifier,
|
|
const char *format, ...)
|
|
{
|
|
FunctionRNA *func;
|
|
|
|
func = RNA_struct_find_function(ptr->type, identifier);
|
|
|
|
if (func) {
|
|
va_list args;
|
|
int ret;
|
|
|
|
va_start(args, format);
|
|
|
|
ret = RNA_function_call_direct_va(C, reports, ptr, func, format, args);
|
|
|
|
va_end(args);
|
|
|
|
return ret;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static int rna_function_format_array_length(const char *format, int ofs, int flen)
|
|
{
|
|
char lenbuf[16];
|
|
int idx = 0;
|
|
|
|
if (format[ofs++] == '[')
|
|
for (; ofs < flen && format[ofs] != ']' && idx < sizeof(lenbuf) - 1; idx++, ofs++)
|
|
lenbuf[idx] = format[ofs];
|
|
|
|
if (ofs < flen && format[ofs + 1] == ']') {
|
|
/* XXX put better error reporting for (ofs >= flen) or idx over lenbuf capacity */
|
|
lenbuf[idx] = '\0';
|
|
return atoi(lenbuf);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rna_function_parameter_parse(PointerRNA *ptr, PropertyRNA *prop, PropertyType type,
|
|
char ftype, int len, void *dest, const void *src, StructRNA *srna,
|
|
const char *tid, const char *fid, const char *pid)
|
|
{
|
|
/* ptr is always a function pointer, prop always a parameter */
|
|
|
|
switch (type) {
|
|
case PROP_BOOLEAN:
|
|
{
|
|
if (ftype != 'b') {
|
|
fprintf(stderr, "%s.%s: wrong type for parameter %s, a boolean was expected\n", tid, fid, pid);
|
|
return -1;
|
|
}
|
|
|
|
if (len == 0)
|
|
*((bool *)dest) = *((bool *)src);
|
|
else
|
|
memcpy(dest, src, len * sizeof(bool));
|
|
|
|
break;
|
|
}
|
|
case PROP_INT:
|
|
{
|
|
if (ftype != 'i') {
|
|
fprintf(stderr, "%s.%s: wrong type for parameter %s, an integer was expected\n", tid, fid, pid);
|
|
return -1;
|
|
}
|
|
|
|
if (len == 0)
|
|
*((int *)dest) = *((int *)src);
|
|
else
|
|
memcpy(dest, src, len * sizeof(int));
|
|
|
|
break;
|
|
}
|
|
case PROP_FLOAT:
|
|
{
|
|
if (ftype != 'f') {
|
|
fprintf(stderr, "%s.%s: wrong type for parameter %s, a float was expected\n", tid, fid, pid);
|
|
return -1;
|
|
}
|
|
|
|
if (len == 0)
|
|
*((float *)dest) = *((float *)src);
|
|
else
|
|
memcpy(dest, src, len * sizeof(float));
|
|
|
|
break;
|
|
}
|
|
case PROP_STRING:
|
|
{
|
|
if (ftype != 's') {
|
|
fprintf(stderr, "%s.%s: wrong type for parameter %s, a string was expected\n", tid, fid, pid);
|
|
return -1;
|
|
}
|
|
|
|
*((char **)dest) = *((char **)src);
|
|
|
|
break;
|
|
}
|
|
case PROP_ENUM:
|
|
{
|
|
if (ftype != 'e') {
|
|
fprintf(stderr, "%s.%s: wrong type for parameter %s, an enum was expected\n", tid, fid, pid);
|
|
return -1;
|
|
}
|
|
|
|
*((int *)dest) = *((int *)src);
|
|
|
|
break;
|
|
}
|
|
case PROP_POINTER:
|
|
{
|
|
StructRNA *ptype;
|
|
|
|
if (ftype != 'O') {
|
|
fprintf(stderr, "%s.%s: wrong type for parameter %s, an object was expected\n", tid, fid, pid);
|
|
return -1;
|
|
}
|
|
|
|
ptype = RNA_property_pointer_type(ptr, prop);
|
|
|
|
if (prop->flag_parameter & PARM_RNAPTR) {
|
|
*((PointerRNA *)dest) = *((PointerRNA *)src);
|
|
break;
|
|
}
|
|
|
|
if (ptype != srna && !RNA_struct_is_a(srna, ptype)) {
|
|
fprintf(stderr, "%s.%s: wrong type for parameter %s, "
|
|
"an object of type %s was expected, passed an object of type %s\n",
|
|
tid, fid, pid, RNA_struct_identifier(ptype), RNA_struct_identifier(srna));
|
|
return -1;
|
|
}
|
|
|
|
*((void **)dest) = *((void **)src);
|
|
|
|
break;
|
|
}
|
|
case PROP_COLLECTION:
|
|
{
|
|
StructRNA *ptype;
|
|
ListBase *lb, *clb;
|
|
Link *link;
|
|
CollectionPointerLink *clink;
|
|
|
|
if (ftype != 'C') {
|
|
fprintf(stderr, "%s.%s: wrong type for parameter %s, a collection was expected\n", tid, fid, pid);
|
|
return -1;
|
|
}
|
|
|
|
lb = (ListBase *)src;
|
|
clb = (ListBase *)dest;
|
|
ptype = RNA_property_pointer_type(ptr, prop);
|
|
|
|
if (ptype != srna && !RNA_struct_is_a(srna, ptype)) {
|
|
fprintf(stderr, "%s.%s: wrong type for parameter %s, "
|
|
"a collection of objects of type %s was expected, "
|
|
"passed a collection of objects of type %s\n",
|
|
tid, fid, pid, RNA_struct_identifier(ptype), RNA_struct_identifier(srna));
|
|
return -1;
|
|
}
|
|
|
|
for (link = lb->first; link; link = link->next) {
|
|
clink = MEM_callocN(sizeof(CollectionPointerLink), "CCollectionPointerLink");
|
|
RNA_pointer_create(NULL, srna, link, &clink->ptr);
|
|
BLI_addtail(clb, clink);
|
|
}
|
|
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
if (len == 0)
|
|
fprintf(stderr, "%s.%s: unknown type for parameter %s\n", tid, fid, pid);
|
|
else
|
|
fprintf(stderr, "%s.%s: unknown array type for parameter %s\n", tid, fid, pid);
|
|
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int RNA_function_call_direct_va(bContext *C, ReportList *reports, PointerRNA *ptr, FunctionRNA *func,
|
|
const char *format, va_list args)
|
|
{
|
|
PointerRNA funcptr;
|
|
ParameterList parms;
|
|
ParameterIterator iter;
|
|
PropertyRNA *pret, *parm;
|
|
PropertyType type;
|
|
int i, ofs, flen, flag_parameter, len, alen, err = 0;
|
|
const char *tid, *fid, *pid = NULL;
|
|
char ftype;
|
|
void **retdata = NULL;
|
|
|
|
RNA_pointer_create(NULL, &RNA_Function, func, &funcptr);
|
|
|
|
tid = RNA_struct_identifier(ptr->type);
|
|
fid = RNA_function_identifier(func);
|
|
pret = func->c_ret;
|
|
flen = strlen(format);
|
|
|
|
RNA_parameter_list_create(&parms, ptr, func);
|
|
RNA_parameter_list_begin(&parms, &iter);
|
|
|
|
for (i = 0, ofs = 0; iter.valid; RNA_parameter_list_next(&iter), i++) {
|
|
parm = iter.parm;
|
|
flag_parameter = RNA_parameter_flag(parm);
|
|
|
|
if (parm == pret) {
|
|
retdata = iter.data;
|
|
continue;
|
|
}
|
|
else if (flag_parameter & PARM_OUTPUT) {
|
|
continue;
|
|
}
|
|
|
|
pid = RNA_property_identifier(parm);
|
|
|
|
if (ofs >= flen || format[ofs] == 'N') {
|
|
if (parm->flag_parameter & PARM_REQUIRED) {
|
|
err = -1;
|
|
fprintf(stderr, "%s.%s: missing required parameter %s\n", tid, fid, pid);
|
|
break;
|
|
}
|
|
ofs++;
|
|
continue;
|
|
}
|
|
|
|
type = RNA_property_type(parm);
|
|
ftype = format[ofs++];
|
|
len = RNA_property_array_length(&funcptr, parm);
|
|
alen = rna_function_format_array_length(format, ofs, flen);
|
|
|
|
if (len != alen) {
|
|
err = -1;
|
|
fprintf(stderr, "%s.%s: for parameter %s, "
|
|
"was expecting an array of %i elements, "
|
|
"passed %i elements instead\n",
|
|
tid, fid, pid, len, alen);
|
|
break;
|
|
}
|
|
|
|
switch (type) {
|
|
case PROP_BOOLEAN:
|
|
case PROP_INT:
|
|
case PROP_ENUM:
|
|
{
|
|
int arg = va_arg(args, int);
|
|
err = rna_function_parameter_parse(&funcptr, parm, type, ftype, len, iter.data, &arg,
|
|
NULL, tid, fid, pid);
|
|
break;
|
|
}
|
|
case PROP_FLOAT:
|
|
{
|
|
double arg = va_arg(args, double);
|
|
err = rna_function_parameter_parse(&funcptr, parm, type, ftype, len, iter.data, &arg,
|
|
NULL, tid, fid, pid);
|
|
break;
|
|
}
|
|
case PROP_STRING:
|
|
{
|
|
const char *arg = va_arg(args, char *);
|
|
err = rna_function_parameter_parse(&funcptr, parm, type, ftype, len, iter.data, &arg,
|
|
NULL, tid, fid, pid);
|
|
break;
|
|
}
|
|
case PROP_POINTER:
|
|
{
|
|
StructRNA *srna = va_arg(args, StructRNA *);
|
|
void *arg = va_arg(args, void *);
|
|
err = rna_function_parameter_parse(&funcptr, parm, type, ftype, len, iter.data, &arg,
|
|
srna, tid, fid, pid);
|
|
break;
|
|
}
|
|
case PROP_COLLECTION:
|
|
{
|
|
StructRNA *srna = va_arg(args, StructRNA *);
|
|
ListBase *arg = va_arg(args, ListBase *);
|
|
err = rna_function_parameter_parse(&funcptr, parm, type, ftype, len, iter.data, &arg,
|
|
srna, tid, fid, pid);
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
/* handle errors */
|
|
err = rna_function_parameter_parse(&funcptr, parm, type, ftype, len, iter.data, NULL,
|
|
NULL, tid, fid, pid);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (err != 0)
|
|
break;
|
|
}
|
|
|
|
if (err == 0)
|
|
err = RNA_function_call(C, reports, ptr, func, &parms);
|
|
|
|
/* XXX throw error when more parameters than those needed are passed or leave silent? */
|
|
if (err == 0 && pret && ofs < flen && format[ofs++] == 'R') {
|
|
parm = pret;
|
|
|
|
type = RNA_property_type(parm);
|
|
ftype = format[ofs++];
|
|
len = RNA_property_array_length(&funcptr, parm);
|
|
alen = rna_function_format_array_length(format, ofs, flen);
|
|
|
|
if (len != alen) {
|
|
err = -1;
|
|
fprintf(stderr, "%s.%s: for return parameter %s, "
|
|
"was expecting an array of %i elements, passed %i elements instead\n",
|
|
tid, fid, pid, len, alen);
|
|
}
|
|
else {
|
|
switch (type) {
|
|
case PROP_BOOLEAN:
|
|
case PROP_INT:
|
|
case PROP_ENUM:
|
|
{
|
|
int *arg = va_arg(args, int *);
|
|
err = rna_function_parameter_parse(&funcptr, parm, type, ftype, len, arg, retdata,
|
|
NULL, tid, fid, pid);
|
|
break;
|
|
}
|
|
case PROP_FLOAT:
|
|
{
|
|
float *arg = va_arg(args, float *);
|
|
err = rna_function_parameter_parse(&funcptr, parm, type, ftype, len, arg, retdata,
|
|
NULL, tid, fid, pid);
|
|
break;
|
|
}
|
|
case PROP_STRING:
|
|
{
|
|
char **arg = va_arg(args, char **);
|
|
err = rna_function_parameter_parse(&funcptr, parm, type, ftype, len, arg, retdata,
|
|
NULL, tid, fid, pid);
|
|
break;
|
|
}
|
|
case PROP_POINTER:
|
|
{
|
|
StructRNA *srna = va_arg(args, StructRNA *);
|
|
void **arg = va_arg(args, void **);
|
|
err = rna_function_parameter_parse(&funcptr, parm, type, ftype, len, arg, retdata,
|
|
srna, tid, fid, pid);
|
|
break;
|
|
}
|
|
case PROP_COLLECTION:
|
|
{
|
|
StructRNA *srna = va_arg(args, StructRNA *);
|
|
ListBase **arg = va_arg(args, ListBase **);
|
|
err = rna_function_parameter_parse(&funcptr, parm, type, ftype, len, arg, retdata,
|
|
srna, tid, fid, pid);
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
/* handle errors */
|
|
err = rna_function_parameter_parse(&funcptr, parm, type, ftype, len, NULL, NULL,
|
|
NULL, tid, fid, pid);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
RNA_parameter_list_end(&iter);
|
|
RNA_parameter_list_free(&parms);
|
|
|
|
return err;
|
|
}
|
|
|
|
int RNA_function_call_direct_va_lookup(bContext *C, ReportList *reports, PointerRNA *ptr,
|
|
const char *identifier, const char *format, va_list args)
|
|
{
|
|
FunctionRNA *func;
|
|
|
|
func = RNA_struct_find_function(ptr->type, identifier);
|
|
|
|
if (func)
|
|
return RNA_function_call_direct_va(C, reports, ptr, func, format, args);
|
|
|
|
return 0;
|
|
}
|
|
|
|
const char *RNA_translate_ui_text(
|
|
const char *text, const char *text_ctxt, StructRNA *type, PropertyRNA *prop, int translate)
|
|
{
|
|
return rna_translate_ui_text(text, text_ctxt, type, prop, translate);
|
|
}
|
|
|
|
bool RNA_property_reset(PointerRNA *ptr, PropertyRNA *prop, int index)
|
|
{
|
|
int len;
|
|
|
|
/* get the length of the array to work with */
|
|
len = RNA_property_array_length(ptr, prop);
|
|
|
|
/* get and set the default values as appropriate for the various types */
|
|
switch (RNA_property_type(prop)) {
|
|
case PROP_BOOLEAN:
|
|
if (len) {
|
|
if (index == -1) {
|
|
bool *tmparray = MEM_callocN(sizeof(bool) * len, "reset_defaults - boolean");
|
|
|
|
RNA_property_boolean_get_default_array(ptr, prop, tmparray);
|
|
RNA_property_boolean_set_array(ptr, prop, tmparray);
|
|
|
|
MEM_freeN(tmparray);
|
|
}
|
|
else {
|
|
int value = RNA_property_boolean_get_default_index(ptr, prop, index);
|
|
RNA_property_boolean_set_index(ptr, prop, index, value);
|
|
}
|
|
}
|
|
else {
|
|
int value = RNA_property_boolean_get_default(ptr, prop);
|
|
RNA_property_boolean_set(ptr, prop, value);
|
|
}
|
|
return true;
|
|
case PROP_INT:
|
|
if (len) {
|
|
if (index == -1) {
|
|
int *tmparray = MEM_callocN(sizeof(int) * len, "reset_defaults - int");
|
|
|
|
RNA_property_int_get_default_array(ptr, prop, tmparray);
|
|
RNA_property_int_set_array(ptr, prop, tmparray);
|
|
|
|
MEM_freeN(tmparray);
|
|
}
|
|
else {
|
|
int value = RNA_property_int_get_default_index(ptr, prop, index);
|
|
RNA_property_int_set_index(ptr, prop, index, value);
|
|
}
|
|
}
|
|
else {
|
|
int value = RNA_property_int_get_default(ptr, prop);
|
|
RNA_property_int_set(ptr, prop, value);
|
|
}
|
|
return true;
|
|
case PROP_FLOAT:
|
|
if (len) {
|
|
if (index == -1) {
|
|
float *tmparray = MEM_callocN(sizeof(float) * len, "reset_defaults - float");
|
|
|
|
RNA_property_float_get_default_array(ptr, prop, tmparray);
|
|
RNA_property_float_set_array(ptr, prop, tmparray);
|
|
|
|
MEM_freeN(tmparray);
|
|
}
|
|
else {
|
|
float value = RNA_property_float_get_default_index(ptr, prop, index);
|
|
RNA_property_float_set_index(ptr, prop, index, value);
|
|
}
|
|
}
|
|
else {
|
|
float value = RNA_property_float_get_default(ptr, prop);
|
|
RNA_property_float_set(ptr, prop, value);
|
|
}
|
|
return true;
|
|
case PROP_ENUM:
|
|
{
|
|
int value = RNA_property_enum_get_default(ptr, prop);
|
|
RNA_property_enum_set(ptr, prop, value);
|
|
return true;
|
|
}
|
|
|
|
case PROP_STRING:
|
|
{
|
|
char *value = RNA_property_string_get_default_alloc(ptr, prop, NULL, 0);
|
|
RNA_property_string_set(ptr, prop, value);
|
|
MEM_freeN(value);
|
|
return true;
|
|
}
|
|
|
|
case PROP_POINTER:
|
|
{
|
|
PointerRNA value = RNA_property_pointer_get_default(ptr, prop);
|
|
RNA_property_pointer_set(ptr, prop, value);
|
|
return true;
|
|
}
|
|
|
|
default:
|
|
/* FIXME: are there still any cases that haven't been handled? comment out "default" block to check :) */
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool RNA_property_assign_default(PointerRNA *ptr, PropertyRNA *prop)
|
|
{
|
|
if (!RNA_property_is_idprop(prop) || RNA_property_array_check(prop)) {
|
|
return false;
|
|
}
|
|
|
|
/* get and set the default values as appropriate for the various types */
|
|
switch (RNA_property_type(prop)) {
|
|
case PROP_INT:
|
|
{
|
|
int value = RNA_property_int_get(ptr, prop);
|
|
return RNA_property_int_set_default(ptr, prop, value);
|
|
}
|
|
|
|
case PROP_FLOAT:
|
|
{
|
|
float value = RNA_property_float_get(ptr, prop);
|
|
return RNA_property_float_set_default(ptr, prop, value);
|
|
}
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static bool rna_property_override_operation_apply(
|
|
Main *bmain,
|
|
PointerRNA *ptr_local, PointerRNA *ptr_override, PointerRNA *ptr_storage,
|
|
PropertyRNA *prop_local, PropertyRNA *prop_override, PropertyRNA *prop_storage,
|
|
PointerRNA *ptr_item_local, PointerRNA *ptr_item_override, PointerRNA *ptr_item_storage,
|
|
IDOverrideStaticPropertyOperation *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;
|
|
}
|
|
|
|
IDOverrideStaticPropertyOperation opop = {
|
|
.operation = IDOVERRIDESTATIC_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);
|
|
}
|
|
|
|
/* use RNA_warning macro which includes __func__ suffix */
|
|
void _RNA_warning(const char *format, ...)
|
|
{
|
|
va_list args;
|
|
|
|
va_start(args, format);
|
|
vprintf(format, args);
|
|
va_end(args);
|
|
|
|
/* gcc macro adds '\n', but cant use for other compilers */
|
|
#ifndef __GNUC__
|
|
fputc('\n', stdout);
|
|
#endif
|
|
|
|
#ifdef WITH_PYTHON
|
|
{
|
|
extern void PyC_LineSpit(void);
|
|
PyC_LineSpit();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static int rna_property_override_diff(
|
|
Main *bmain,
|
|
PointerRNA *ptr_a, PointerRNA *ptr_b, PropertyRNA *prop, PropertyRNA *prop_a, PropertyRNA *prop_b, const char *rna_path,
|
|
eRNACompareMode mode, IDOverrideStatic *override, const int 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));
|
|
|
|
return (rna_property_override_diff(bmain, ptr_a, ptr_b, prop, NULL, NULL, NULL, 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;
|
|
else if (ptr_a == NULL || ptr_b == NULL)
|
|
return false;
|
|
else 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 exptected to be an 'un-resolved' one,
|
|
* while the two laters 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,
|
|
PointerRNA *ptr_a, PointerRNA *ptr_b, PropertyRNA *prop, PropertyRNA *prop_a, PropertyRNA *prop_b,
|
|
const char *rna_path, eRNACompareMode mode,
|
|
IDOverrideStatic *override, const int flags, eRNAOverrideMatchResult *r_report_flags)
|
|
{
|
|
if (prop != NULL) {
|
|
BLI_assert(prop_a == NULL && prop_b == NULL);
|
|
prop_a = prop;
|
|
prop_b = prop;
|
|
}
|
|
|
|
if (ELEM(NULL, prop_a, prop_b)) {
|
|
return (prop_a == prop_b) ? 0 : 1;
|
|
}
|
|
|
|
if (!RNA_property_comparable(ptr_a, prop_a) || !RNA_property_comparable(ptr_b, prop_b)) {
|
|
return 0;
|
|
}
|
|
|
|
if (mode == RNA_EQ_UNSET_MATCH_ANY) {
|
|
/* uninitialized properties are assumed to match anything */
|
|
if (!RNA_property_is_set(ptr_a, prop_a) || !RNA_property_is_set(ptr_b, prop_b)) {
|
|
return 0;
|
|
}
|
|
}
|
|
else if (mode == RNA_EQ_UNSET_MATCH_NONE) {
|
|
/* unset properties never match set properties */
|
|
if (RNA_property_is_set(ptr_a, prop_a) != RNA_property_is_set(ptr_b, prop_b)) {
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
if (prop != NULL) {
|
|
/* Ensure we get real property data, be it an actual RNA property, or an IDProperty in disguise. */
|
|
prop_a = rna_ensure_property_realdata(&prop_a, ptr_a);
|
|
prop_b = rna_ensure_property_realdata(&prop_b, ptr_b);
|
|
|
|
if (ELEM(NULL, prop_a, prop_b)) {
|
|
return (prop_a == prop_b) ? 0 : 1;
|
|
}
|
|
}
|
|
|
|
/* Check if we are working with arrays. */
|
|
const bool is_array_a = RNA_property_array_check(prop_a);
|
|
const bool is_array_b = RNA_property_array_check(prop_b);
|
|
|
|
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 int len_a = RNA_property_array_length(ptr_a, prop_a);
|
|
const int len_b = RNA_property_array_length(ptr_b, prop_b);
|
|
|
|
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->magic != RNA_MAGIC) {
|
|
override_diff = rna_property_override_diff_default;
|
|
if (prop_b->magic == RNA_MAGIC && prop_b->override_diff != override_diff) {
|
|
override_diff = NULL;
|
|
}
|
|
}
|
|
else if (prop_b->magic != RNA_MAGIC) {
|
|
override_diff = rna_property_override_diff_default;
|
|
if (prop_a->override_diff != override_diff) {
|
|
override_diff = NULL;
|
|
}
|
|
}
|
|
else if (prop_a->override_diff == prop_b->override_diff) {
|
|
override_diff = prop_a->override_diff;
|
|
}
|
|
|
|
if (override_diff == NULL) {
|
|
#ifndef NDEBUG
|
|
printf("'%s' gives unmatching or NULL RNA diff callbacks, should not happen (%d vs. %d).\n",
|
|
rna_path ? rna_path : (prop_a->magic != RNA_MAGIC ? ((IDProperty *)prop_a)->name : prop_a->identifier),
|
|
prop_a->magic == RNA_MAGIC, prop_b->magic == RNA_MAGIC);
|
|
#endif
|
|
BLI_assert(0);
|
|
return 1;
|
|
}
|
|
|
|
bool override_changed = false;
|
|
int diff_flags = flags;
|
|
if (!RNA_property_overridable_get(ptr_a, prop_a)) {
|
|
diff_flags &= ~RNA_OVERRIDE_COMPARE_CREATE;
|
|
}
|
|
const int diff = override_diff(
|
|
bmain,
|
|
ptr_a, ptr_b, prop_a, prop_b, len_a, len_b,
|
|
mode, override, rna_path, 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,
|
|
IDOverrideStaticProperty *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;
|
|
}
|
|
|
|
BLI_assert(prop_local->override_store == prop_reference->override_store &&
|
|
(!ptr_storage || prop_local->override_store == prop_storage->override_store) &&
|
|
prop_local->override_store != NULL);
|
|
|
|
for (IDOverrideStaticPropertyOperation *opop = op->operations.first; opop; opop = opop->next) {
|
|
/* Only needed for diff operations. */
|
|
if (!ELEM(opop->operation, IDOVERRIDESTATIC_OP_ADD, IDOVERRIDESTATIC_OP_SUBTRACT, IDOVERRIDESTATIC_OP_MULTIPLY)) {
|
|
continue;
|
|
}
|
|
|
|
if (prop_local->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_local, PointerRNA *ptr_override, PointerRNA *ptr_storage,
|
|
PropertyRNA *prop_local, PropertyRNA *prop_override, PropertyRNA *prop_storage,
|
|
PointerRNA *ptr_item_local, PointerRNA *ptr_item_override, PointerRNA *ptr_item_storage,
|
|
IDOverrideStaticPropertyOperation *opop)
|
|
{
|
|
int len_local, len_reference, len_storage = 0;
|
|
|
|
const short override_op = opop->operation;
|
|
|
|
if (override_op == IDOVERRIDESTATIC_OP_NOOP) {
|
|
return true;
|
|
}
|
|
|
|
if (ELEM(override_op, IDOVERRIDESTATIC_OP_ADD, IDOVERRIDESTATIC_OP_SUBTRACT, IDOVERRIDESTATIC_OP_MULTIPLY) && !ptr_storage) {
|
|
/* We cannot apply 'diff' override operations without some reference storage.
|
|
* This should typically only happen at read time of .blend file... */
|
|
return false;
|
|
}
|
|
|
|
if (ELEM(override_op, IDOVERRIDESTATIC_OP_ADD, IDOVERRIDESTATIC_OP_SUBTRACT, IDOVERRIDESTATIC_OP_MULTIPLY) && !prop_storage) {
|
|
/* We cannot apply 'diff' override operations without some reference storage.
|
|
* This should typically only happen at read time of .blend file... */
|
|
return false;
|
|
}
|
|
|
|
RNAPropOverrideApply override_apply = NULL;
|
|
/* Special case for IDProps, we use default callback then. */
|
|
if (prop_local->magic != RNA_MAGIC) {
|
|
override_apply = rna_property_override_apply_default;
|
|
if (prop_override->magic == RNA_MAGIC && prop_override->override_apply != override_apply) {
|
|
override_apply = NULL;
|
|
}
|
|
}
|
|
else if (prop_override->magic != RNA_MAGIC) {
|
|
override_apply = rna_property_override_apply_default;
|
|
if (prop_local->override_apply != override_apply) {
|
|
override_apply = NULL;
|
|
}
|
|
}
|
|
else if (prop_local->override_apply == prop_override->override_apply) {
|
|
override_apply = prop_local->override_apply;
|
|
}
|
|
|
|
if (ptr_storage && prop_storage->magic == RNA_MAGIC && prop_storage->override_apply != override_apply) {
|
|
override_apply = NULL;
|
|
}
|
|
|
|
if (override_apply == NULL) {
|
|
#ifndef NDEBUG
|
|
printf("'%s' gives unmatching or NULL RNA copy callbacks, should not happen (%d vs. %d).\n",
|
|
prop_local->magic != RNA_MAGIC ? ((IDProperty *)prop_local)->name : prop_local->identifier,
|
|
prop_local->magic == RNA_MAGIC, prop_override->magic == RNA_MAGIC);
|
|
#endif
|
|
BLI_assert(0);
|
|
return false;
|
|
}
|
|
|
|
/* 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_override, prop_override);
|
|
if (ptr_storage) {
|
|
len_storage = RNA_property_array_length(ptr_storage, prop_storage);
|
|
}
|
|
|
|
if (len_local != len_reference || (ptr_storage && len_local != 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 */
|
|
return override_apply(
|
|
bmain,
|
|
ptr_local, ptr_override, ptr_storage,
|
|
prop_local, prop_override, prop_storage,
|
|
len_local, len_reference, len_storage,
|
|
ptr_item_local, ptr_item_override, ptr_item_storage,
|
|
opop);
|
|
}
|
|
|
|
/**
|
|
* 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,
|
|
IDOverrideStatic *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->id.data && ptr_reference->id.data);
|
|
|
|
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;
|
|
|
|
//#define DEBUG_OVERRIDE_TIMEIT
|
|
#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
|
|
|
|
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 *prop_local = iter.ptr.data;
|
|
PropertyRNA *prop_reference = iter.ptr.data;
|
|
|
|
/* Ensure we get real property data, be it an actual RNA property, or an IDProperty in disguise. */
|
|
prop_local = rna_ensure_property_realdata(&prop_local, ptr_local);
|
|
prop_reference = rna_ensure_property_realdata(&prop_reference, ptr_reference);
|
|
|
|
if (ELEM(NULL, prop_local, prop_reference)) {
|
|
continue;
|
|
}
|
|
|
|
if (ignore_non_overridable && !RNA_property_overridable_get(ptr_local, prop_local)) {
|
|
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
|
|
#define RNA_PATH_PRINTF(_str, ...) \
|
|
if (BLI_snprintf(rna_path, RNA_PATH_BUFFSIZE, \
|
|
(_str), __VA_ARGS__) >= RNA_PATH_BUFFSIZE) \
|
|
{ rna_path = BLI_sprintfN((_str), __VA_ARGS__); }(void)0
|
|
#define RNA_PATH_FREE \
|
|
if (rna_path != rna_path_buffer) MEM_freeN(rna_path)
|
|
|
|
char rna_path_buffer[RNA_PATH_BUFFSIZE];
|
|
char *rna_path = rna_path_buffer;
|
|
|
|
/* XXX TODO this will have to be refined to handle collections insertions, and array items */
|
|
if (root_path) {
|
|
/* Inlined building, much much more efficient. */
|
|
if (prop_local->magic == RNA_MAGIC) {
|
|
RNA_PATH_PRINTF("%s.%s", root_path, RNA_property_identifier(prop_local));
|
|
}
|
|
else {
|
|
RNA_PATH_PRINTF("%s[\"%s\"]", root_path, RNA_property_identifier(prop_local));
|
|
}
|
|
}
|
|
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, prop_local);
|
|
}
|
|
if (rna_path == NULL) {
|
|
continue;
|
|
}
|
|
|
|
// printf("Override Checking %s\n", rna_path);
|
|
|
|
if (ignore_overridden && BKE_override_static_property_find(override, rna_path) != NULL) {
|
|
RNA_PATH_FREE;
|
|
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,
|
|
ptr_local, ptr_reference, NULL, prop_local, prop_reference, rna_path,
|
|
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... */
|
|
IDOverrideStaticProperty *op = BKE_override_static_property_find(override, rna_path);
|
|
IDOverrideStaticPropertyOperation *opop = op ? op->operations.first : NULL;
|
|
|
|
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 == IDOVERRIDESTATIC_OP_NOOP) {
|
|
/* We should restore that property to its reference value */
|
|
if (RNA_property_editable(ptr_local, prop_local)) {
|
|
IDOverrideStaticPropertyOperation opop_tmp = {
|
|
.operation = IDOVERRIDESTATIC_OP_REPLACE,
|
|
.subitem_reference_index = -1,
|
|
.subitem_local_index = -1,
|
|
};
|
|
rna_property_override_operation_apply(
|
|
bmain,
|
|
ptr_local, ptr_reference, NULL,
|
|
prop_local, prop_reference, 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. */
|
|
/* BLI_assert(!"We have differences between reference and overriding data on non-editable property."); */
|
|
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. */
|
|
MEM_SAFE_FREE(rna_path);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
RNA_PATH_FREE;
|
|
|
|
#undef RNA_PATH_BUFFSIZE
|
|
#undef RNA_PATH_PRINTF
|
|
#undef RNA_PATH_FREE
|
|
}
|
|
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->id.data)->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, IDOverrideStatic *override)
|
|
{
|
|
bool changed = false;
|
|
|
|
#ifdef DEBUG_OVERRIDE_TIMEIT
|
|
TIMEIT_START_AVERAGED(RNA_struct_override_store);
|
|
#endif
|
|
for (IDOverrideStaticProperty *op = override->properties.first; op; op = op->next) {
|
|
/* 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->id.data != 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_property_override_apply_ex(
|
|
Main *bmain,
|
|
PointerRNA *ptr_local, PointerRNA *ptr_override, PointerRNA *ptr_storage,
|
|
PropertyRNA *prop_local, PropertyRNA *prop_override, PropertyRNA *prop_storage,
|
|
PointerRNA *ptr_item_local, PointerRNA *ptr_item_override, PointerRNA *ptr_item_storage,
|
|
IDOverrideStaticProperty *op, const bool do_insert)
|
|
{
|
|
for (IDOverrideStaticPropertyOperation *opop = op->operations.first; opop; opop = opop->next) {
|
|
if (!do_insert != !ELEM(opop->operation, IDOVERRIDESTATIC_OP_INSERT_AFTER, IDOVERRIDESTATIC_OP_INSERT_BEFORE)) {
|
|
if (!do_insert) {
|
|
printf("Skipping insert override operations in first pass (%s)!\n", op->rna_path);
|
|
}
|
|
continue;
|
|
}
|
|
if (!rna_property_override_operation_apply(
|
|
bmain,
|
|
ptr_local, ptr_override, ptr_storage,
|
|
prop_local, prop_override, prop_storage,
|
|
ptr_item_local, ptr_item_override, ptr_item_storage,
|
|
opop))
|
|
{
|
|
/* TODO No assert here, would be much much better to just report as warning,
|
|
* failing override applications will probably be fairly common! */
|
|
BLI_assert(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Apply given \a override operations on \a ptr_local, using \a ptr_override
|
|
* (and \a ptr_storage form differential ops) as source. */
|
|
void RNA_struct_override_apply(
|
|
Main *bmain,
|
|
PointerRNA *ptr_local, PointerRNA *ptr_override, PointerRNA *ptr_storage, IDOverrideStatic *override)
|
|
{
|
|
#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 inneficient 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) {
|
|
for (IDOverrideStaticProperty *op = override->properties.first; op; op = op->next) {
|
|
/* Simplified for now! */
|
|
PointerRNA data_override, data_local;
|
|
PointerRNA data_item_override, data_item_local;
|
|
PropertyRNA *prop_override, *prop_local;
|
|
|
|
if (RNA_path_resolve_property_and_item_pointer(
|
|
ptr_local, op->rna_path, &data_local, &prop_local, &data_item_local) &&
|
|
RNA_path_resolve_property_and_item_pointer(
|
|
ptr_override, op->rna_path, &data_override, &prop_override, &data_item_override))
|
|
{
|
|
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->id.data != NULL)) {
|
|
RNA_path_resolve_property_and_item_pointer(
|
|
ptr_storage, op->rna_path, &data_storage, &prop_storage, &data_item_storage);
|
|
}
|
|
|
|
rna_property_override_apply_ex(
|
|
bmain,
|
|
&data_local, &data_override, prop_storage ? &data_storage : NULL,
|
|
prop_local, prop_override, prop_storage,
|
|
&data_item_local, &data_item_override, prop_storage ? &data_item_storage : NULL,
|
|
op, do_insert);
|
|
}
|
|
#ifndef NDEBUG
|
|
else {
|
|
printf("Failed to apply static override operation to '%s.%s' "
|
|
"(could not resolve some properties, local: %d, override: %d)\n",
|
|
((ID *)ptr_override->id.data)->name, op->rna_path,
|
|
RNA_path_resolve_property(ptr_local, op->rna_path, &data_local, &prop_local),
|
|
RNA_path_resolve_property(ptr_override, op->rna_path, &data_override, &prop_override));
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
#ifdef DEBUG_OVERRIDE_TIMEIT
|
|
TIMEIT_END_AVERAGED(RNA_struct_override_apply);
|
|
#endif
|
|
}
|
|
|
|
IDOverrideStaticProperty *RNA_property_override_property_find(PointerRNA *ptr, PropertyRNA *prop)
|
|
{
|
|
ID *id = ptr->id.data;
|
|
|
|
if (!id || !id->override_static) {
|
|
return NULL;
|
|
}
|
|
|
|
char *rna_path = RNA_path_from_ID_to_property(ptr, prop);
|
|
if (rna_path) {
|
|
IDOverrideStaticProperty *op = BKE_override_static_property_find(id->override_static, rna_path);
|
|
MEM_freeN(rna_path);
|
|
return op;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
IDOverrideStaticProperty *RNA_property_override_property_get(PointerRNA *ptr, PropertyRNA *prop, bool *r_created)
|
|
{
|
|
ID *id = ptr->id.data;
|
|
|
|
if (!id || !id->override_static) {
|
|
return NULL;
|
|
}
|
|
|
|
char *rna_path = RNA_path_from_ID_to_property(ptr, prop);
|
|
if (rna_path) {
|
|
IDOverrideStaticProperty *op = BKE_override_static_property_get(id->override_static, rna_path, r_created);
|
|
MEM_freeN(rna_path);
|
|
return op;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
IDOverrideStaticPropertyOperation *RNA_property_override_property_operation_find(
|
|
PointerRNA *ptr, PropertyRNA *prop, const int index, const bool strict, bool *r_strict)
|
|
{
|
|
IDOverrideStaticProperty *op = RNA_property_override_property_find(ptr, prop);
|
|
|
|
if (!op) {
|
|
return NULL;
|
|
}
|
|
|
|
return BKE_override_static_property_operation_find(op, NULL, NULL, index, index, strict, r_strict);
|
|
}
|
|
|
|
IDOverrideStaticPropertyOperation *RNA_property_override_property_operation_get(
|
|
PointerRNA *ptr, PropertyRNA *prop, const short operation, const int index,
|
|
const bool strict, bool *r_strict, bool *r_created)
|
|
{
|
|
IDOverrideStaticProperty *op = RNA_property_override_property_get(ptr, prop, NULL);
|
|
|
|
if (!op) {
|
|
return NULL;
|
|
}
|
|
|
|
return BKE_override_static_property_operation_get(op, operation, NULL, NULL, index, index, strict, r_strict, r_created);
|
|
}
|
|
|
|
eRNAOverrideStatus RNA_property_static_override_status(PointerRNA *ptr, PropertyRNA *prop, const int index)
|
|
{
|
|
int override_status = 0;
|
|
|
|
if (!BKE_override_static_is_enabled()) {
|
|
return override_status;
|
|
}
|
|
|
|
if (!ptr || !prop || !ptr->id.data || !((ID *)ptr->id.data)->override_static) {
|
|
return override_status;
|
|
}
|
|
|
|
if (RNA_property_overridable_get(ptr, prop) && RNA_property_editable_flag(ptr, prop)) {
|
|
override_status |= RNA_OVERRIDE_STATUS_OVERRIDABLE;
|
|
}
|
|
|
|
IDOverrideStaticPropertyOperation *opop = RNA_property_override_property_operation_find(ptr, prop, index, false, NULL);
|
|
if (opop != NULL) {
|
|
override_status |= RNA_OVERRIDE_STATUS_OVERRIDDEN;
|
|
if (opop->flag & IDOVERRIDESTATIC_FLAG_MANDATORY) {
|
|
override_status |= RNA_OVERRIDE_STATUS_MANDATORY;
|
|
}
|
|
if (opop->flag & IDOVERRIDESTATIC_FLAG_LOCKED) {
|
|
override_status |= RNA_OVERRIDE_STATUS_LOCKED;
|
|
}
|
|
}
|
|
|
|
return override_status;
|
|
}
|
|
|
|
|
|
|
|
bool RNA_path_resolved_create(
|
|
PointerRNA *ptr, struct PropertyRNA *prop,
|
|
const int prop_index,
|
|
PathResolvedRNA *r_anim_rna)
|
|
{
|
|
int array_len = RNA_property_array_length(ptr, prop);
|
|
|
|
if ((array_len == 0) || (prop_index < array_len)) {
|
|
r_anim_rna->ptr = *ptr;
|
|
r_anim_rna->prop = prop;
|
|
r_anim_rna->prop_index = array_len ? prop_index : -1;
|
|
|
|
return true;
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static char rna_struct_state_owner[64];
|
|
void RNA_struct_state_owner_set(const char *name)
|
|
{
|
|
if (name) {
|
|
BLI_strncpy(rna_struct_state_owner, name, sizeof(rna_struct_state_owner));
|
|
}
|
|
else {
|
|
rna_struct_state_owner[0] = '\0';
|
|
}
|
|
}
|
|
|
|
const char *RNA_struct_state_owner_get(void)
|
|
{
|
|
if (rna_struct_state_owner[0]) {
|
|
return rna_struct_state_owner;
|
|
}
|
|
return NULL;
|
|
}
|