This repository has been archived on 2023-10-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files
blender-archive/source/blender/makesrna/intern/rna_access.c

8299 lines
215 KiB
C
Raw Normal View History

/*
* 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,
2010-02-12 13:34:04 +00:00
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/** \file
* \ingroup RNA
2011-02-27 20:20:01 +00:00
*/
#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
2011-06-20 10:07:46 +00:00
#include "BLF_api.h"
#include "BLT_translation.h"
2011-06-20 10:07:46 +00:00
#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"
2014-08-13 08:37:41 +10:00
#include "RNA_enum_types.h"
2013-03-07 02:44:55 +00:00
#include "WM_api.h"
#include "WM_message.h"
2013-03-07 02:44:55 +00:00
/* 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;
2018-06-09 14:40:09 +02:00
RNA_property_update_cache_free();
2018-06-09 14:40:09 +02:00
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);
2018-06-09 14:40:09 +02:00
while (idtype->refine) {
type = idtype->refine(&tmp);
if (type == idtype)
break;
else
idtype = type;
}
}
2018-06-09 14:40:09 +02:00
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;
}
2013-03-07 02:44:55 +00:00
IDProperty *RNA_struct_idprops(PointerRNA *ptr, bool create)
{
StructRNA *type = ptr->type;
if (type && type->idproperties) {
return type->idproperties(ptr, create);
}
2018-06-09 14:40:09 +02:00
return NULL;
}
2013-03-07 02:44:55 +00:00
bool RNA_struct_idprops_check(StructRNA *srna)
{
2013-03-07 02:44:55 +00:00
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;
}
Merge of the PyNodes branch (aka "custom nodes") into trunk. PyNodes opens up the node system in Blender to scripters and adds a number of UI-level improvements. === Dynamic node type registration === Node types can now be added at runtime, using the RNA registration mechanism from python. This enables addons such as render engines to create a complete user interface with nodes. Examples of how such nodes can be defined can be found in my personal wiki docs atm [1] and as a script template in release/scripts/templates_py/custom_nodes.py [2]. === Node group improvements === Each node editor now has a tree history of edited node groups, which allows opening and editing nested node groups. The node editor also supports pinning now, so that different spaces can be used to edit different node groups simultaneously. For more ramblings and rationale see (really old) blog post on code.blender.org [3]. The interface of node groups has been overhauled. Sockets of a node group are no longer displayed in columns on either side, but instead special input/output nodes are used to mirror group sockets inside a node tree. This solves the problem of long node lines in groups and allows more adaptable node layout. Internal sockets can be exposed from a group by either connecting to the extension sockets in input/output nodes (shown as empty circle) or by adding sockets from the node property bar in the "Interface" panel. Further details such as the socket name can also be changed there. [1] http://wiki.blender.org/index.php/User:Phonybone/Python_Nodes [2] http://projects.blender.org/scm/viewvc.php/trunk/blender/release/scripts/templates_py/custom_nodes.py?view=markup&root=bf-blender [3] http://code.blender.org/index.php/2012/01/improving-node-group-interface-editing/
2013-03-18 16:34:57 +00:00
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);
Merge of the PyNodes branch (aka "custom nodes") into trunk. PyNodes opens up the node system in Blender to scripters and adds a number of UI-level improvements. === Dynamic node type registration === Node types can now be added at runtime, using the RNA registration mechanism from python. This enables addons such as render engines to create a complete user interface with nodes. Examples of how such nodes can be defined can be found in my personal wiki docs atm [1] and as a script template in release/scripts/templates_py/custom_nodes.py [2]. === Node group improvements === Each node editor now has a tree history of edited node groups, which allows opening and editing nested node groups. The node editor also supports pinning now, so that different spaces can be used to edit different node groups simultaneously. For more ramblings and rationale see (really old) blog post on code.blender.org [3]. The interface of node groups has been overhauled. Sockets of a node group are no longer displayed in columns on either side, but instead special input/output nodes are used to mirror group sockets inside a node tree. This solves the problem of long node lines in groups and allows more adaptable node layout. Internal sockets can be exposed from a group by either connecting to the extension sockets in input/output nodes (shown as empty circle) or by adding sockets from the node property bar in the "Interface" panel. Further details such as the socket name can also be changed there. [1] http://wiki.blender.org/index.php/User:Phonybone/Python_Nodes [2] http://projects.blender.org/scm/viewvc.php/trunk/blender/release/scripts/templates_py/custom_nodes.py?view=markup&root=bf-blender [3] http://code.blender.org/index.php/2012/01/improving-node-group-interface-editing/
2013-03-18 16:34:57 +00:00
}
}
}
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;
}
}
2013-03-07 02:44:55 +00:00
static bool rna_ensure_property_array_check(PropertyRNA *prop)
{
if (prop->magic == RNA_MAGIC) {
2013-03-07 02:44:55 +00:00
return (prop->getlength || prop->totarraylength);
}
else {
IDProperty *idprop = (IDProperty *)prop;
2013-03-07 02:44:55 +00:00
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;
}
}
2013-03-07 02:44:55 +00:00
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 */
2018-06-09 14:40:09 +02:00
switch (idprop->type) {
case IDP_IDPARRAY:
if (prop->type != PROP_COLLECTION)
2013-03-07 02:44:55 +00:00
return false;
break;
case IDP_ARRAY:
if (rna_ensure_property_array_length(ptr, prop) != idprop->len)
2013-03-07 02:44:55 +00:00
return false;
if (idprop->subtype == IDP_FLOAT && prop->type != PROP_FLOAT)
2013-03-07 02:44:55 +00:00
return false;
if (idprop->subtype == IDP_INT && !ELEM(prop->type, PROP_BOOLEAN, PROP_INT, PROP_ENUM))
2013-03-07 02:44:55 +00:00
return false;
break;
case IDP_INT:
if (!ELEM(prop->type, PROP_BOOLEAN, PROP_INT, PROP_ENUM))
2013-03-07 02:44:55 +00:00
return false;
break;
case IDP_FLOAT:
case IDP_DOUBLE:
if (prop->type != PROP_FLOAT)
2013-03-07 02:44:55 +00:00
return false;
break;
case IDP_STRING:
if (prop->type != PROP_STRING)
2013-03-07 02:44:55 +00:00
return false;
break;
case IDP_GROUP:
case IDP_ID:
if (prop->type != PROP_POINTER)
2013-03-07 02:44:55 +00:00
return false;
break;
default:
2013-03-07 02:44:55 +00:00
return false;
}
2013-03-07 02:44:55 +00:00
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;
}
2018-03-15 09:44:03 +01:00
else {
return return_rnaprop ? *prop : NULL;
2018-03-15 09:44:03 +01:00
}
}
{
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)];
}
}
2014-04-22 22:55:10 +10:00
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 */
}
Merge of the PyNodes branch (aka "custom nodes") into trunk. PyNodes opens up the node system in Blender to scripters and adds a number of UI-level improvements. === Dynamic node type registration === Node types can now be added at runtime, using the RNA registration mechanism from python. This enables addons such as render engines to create a complete user interface with nodes. Examples of how such nodes can be defined can be found in my personal wiki docs atm [1] and as a script template in release/scripts/templates_py/custom_nodes.py [2]. === Node group improvements === Each node editor now has a tree history of edited node groups, which allows opening and editing nested node groups. The node editor also supports pinning now, so that different spaces can be used to edit different node groups simultaneously. For more ramblings and rationale see (really old) blog post on code.blender.org [3]. The interface of node groups has been overhauled. Sockets of a node group are no longer displayed in columns on either side, but instead special input/output nodes are used to mirror group sockets inside a node tree. This solves the problem of long node lines in groups and allows more adaptable node layout. Internal sockets can be exposed from a group by either connecting to the extension sockets in input/output nodes (shown as empty circle) or by adding sockets from the node property bar in the "Interface" panel. Further details such as the socket name can also be changed there. [1] http://wiki.blender.org/index.php/User:Phonybone/Python_Nodes [2] http://projects.blender.org/scm/viewvc.php/trunk/blender/release/scripts/templates_py/custom_nodes.py?view=markup&root=bf-blender [3] http://code.blender.org/index.php/2012/01/improving-node-group-interface-editing/
2013-03-18 16:34:57 +00:00
return description;
}
2014-04-22 22:55:10 +10:00
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;
Merge of the PyNodes branch (aka "custom nodes") into trunk. PyNodes opens up the node system in Blender to scripters and adds a number of UI-level improvements. === Dynamic node type registration === Node types can now be added at runtime, using the RNA registration mechanism from python. This enables addons such as render engines to create a complete user interface with nodes. Examples of how such nodes can be defined can be found in my personal wiki docs atm [1] and as a script template in release/scripts/templates_py/custom_nodes.py [2]. === Node group improvements === Each node editor now has a tree history of edited node groups, which allows opening and editing nested node groups. The node editor also supports pinning now, so that different spaces can be used to edit different node groups simultaneously. For more ramblings and rationale see (really old) blog post on code.blender.org [3]. The interface of node groups has been overhauled. Sockets of a node group are no longer displayed in columns on either side, but instead special input/output nodes are used to mirror group sockets inside a node tree. This solves the problem of long node lines in groups and allows more adaptable node layout. Internal sockets can be exposed from a group by either connecting to the extension sockets in input/output nodes (shown as empty circle) or by adding sockets from the node property bar in the "Interface" panel. Further details such as the socket name can also be changed there. [1] http://wiki.blender.org/index.php/User:Phonybone/Python_Nodes [2] http://projects.blender.org/scm/viewvc.php/trunk/blender/release/scripts/templates_py/custom_nodes.py?view=markup&root=bf-blender [3] http://code.blender.org/index.php/2012/01/improving-node-group-interface-editing/
2013-03-18 16:34:57 +00:00
return name;
}
/* Structs */
StructRNA *RNA_struct_find(const char *identifier)
{
return BLI_ghash_lookup(BLENDER_RNA.structs_map, identifier);
}
2014-04-22 22:55:10 +10:00
const char *RNA_struct_identifier(const StructRNA *type)
{
return type->identifier;
}
2014-04-22 22:55:10 +10:00
const char *RNA_struct_ui_name(const StructRNA *type)
{
return CTX_IFACE_(type->translation_context, type->name);
}
2014-04-22 22:55:10 +10:00
const char *RNA_struct_ui_name_raw(const StructRNA *type)
Merge of the PyNodes branch (aka "custom nodes") into trunk. PyNodes opens up the node system in Blender to scripters and adds a number of UI-level improvements. === Dynamic node type registration === Node types can now be added at runtime, using the RNA registration mechanism from python. This enables addons such as render engines to create a complete user interface with nodes. Examples of how such nodes can be defined can be found in my personal wiki docs atm [1] and as a script template in release/scripts/templates_py/custom_nodes.py [2]. === Node group improvements === Each node editor now has a tree history of edited node groups, which allows opening and editing nested node groups. The node editor also supports pinning now, so that different spaces can be used to edit different node groups simultaneously. For more ramblings and rationale see (really old) blog post on code.blender.org [3]. The interface of node groups has been overhauled. Sockets of a node group are no longer displayed in columns on either side, but instead special input/output nodes are used to mirror group sockets inside a node tree. This solves the problem of long node lines in groups and allows more adaptable node layout. Internal sockets can be exposed from a group by either connecting to the extension sockets in input/output nodes (shown as empty circle) or by adding sockets from the node property bar in the "Interface" panel. Further details such as the socket name can also be changed there. [1] http://wiki.blender.org/index.php/User:Phonybone/Python_Nodes [2] http://projects.blender.org/scm/viewvc.php/trunk/blender/release/scripts/templates_py/custom_nodes.py?view=markup&root=bf-blender [3] http://code.blender.org/index.php/2012/01/improving-node-group-interface-editing/
2013-03-18 16:34:57 +00:00
{
return type->name;
}
2014-04-22 22:55:10 +10:00
int RNA_struct_ui_icon(const StructRNA *type)
{
if (type)
return type->icon;
else
return ICON_DOT;
}
2014-04-22 22:55:10 +10:00
const char *RNA_struct_ui_description(const StructRNA *type)
{
return TIP_(type->description);
}
2014-04-22 22:55:10 +10:00
const char *RNA_struct_ui_description_raw(const StructRNA *type)
Merge of the PyNodes branch (aka "custom nodes") into trunk. PyNodes opens up the node system in Blender to scripters and adds a number of UI-level improvements. === Dynamic node type registration === Node types can now be added at runtime, using the RNA registration mechanism from python. This enables addons such as render engines to create a complete user interface with nodes. Examples of how such nodes can be defined can be found in my personal wiki docs atm [1] and as a script template in release/scripts/templates_py/custom_nodes.py [2]. === Node group improvements === Each node editor now has a tree history of edited node groups, which allows opening and editing nested node groups. The node editor also supports pinning now, so that different spaces can be used to edit different node groups simultaneously. For more ramblings and rationale see (really old) blog post on code.blender.org [3]. The interface of node groups has been overhauled. Sockets of a node group are no longer displayed in columns on either side, but instead special input/output nodes are used to mirror group sockets inside a node tree. This solves the problem of long node lines in groups and allows more adaptable node layout. Internal sockets can be exposed from a group by either connecting to the extension sockets in input/output nodes (shown as empty circle) or by adding sockets from the node property bar in the "Interface" panel. Further details such as the socket name can also be changed there. [1] http://wiki.blender.org/index.php/User:Phonybone/Python_Nodes [2] http://projects.blender.org/scm/viewvc.php/trunk/blender/release/scripts/templates_py/custom_nodes.py?view=markup&root=bf-blender [3] http://code.blender.org/index.php/2012/01/improving-node-group-interface-editing/
2013-03-18 16:34:57 +00:00
{
return type->description;
}
2014-04-22 22:55:10 +10:00
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;
}
2014-04-22 22:55:10 +10:00
bool RNA_struct_is_ID(const StructRNA *type)
{
return (type->flag & STRUCT_ID) != 0;
}
2014-04-22 22:55:10 +10:00
bool RNA_struct_undo_check(const StructRNA *type)
{
return (type->flag & STRUCT_UNDO) != 0;
}
2014-04-22 22:55:10 +10:00
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 */
2013-03-07 02:44:55 +00:00
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);
2013-03-07 02:44:55 +00:00
return true;
}
}
2013-03-07 02:44:55 +00:00
return false;
}
2014-04-22 22:55:10 +10:00
bool RNA_struct_is_a(const StructRNA *type, const StructRNA *srna)
{
2014-04-22 22:55:10 +10:00
const StructRNA *base;
if (srna == &RNA_AnyType)
return true;
if (!type)
2013-03-07 02:44:55 +00:00
return false;
/* ptr->type is always maximally refined */
for (base = type; base; base = base->base)
if (base == srna)
2013-03-07 02:44:55 +00:00
return true;
2018-06-09 14:40:09 +02:00
2013-03-07 02:44:55 +00:00
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;
}
2018-06-09 14:40:09 +02:00
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;
}
2013-03-07 02:44:55 +00:00
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;
2013-03-07 02:44:55 +00:00
bool found = false;
iterprop = RNA_struct_iterator_property(ptr->type);
RNA_PROP_BEGIN (ptr, itemptr, iterprop)
{
2012-05-27 19:40:36 +00:00
/* PropertyRNA *prop = itemptr.data; */
if (prop_test == (PropertyRNA *)itemptr.data) {
2013-03-07 02:44:55 +00:00
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;
2018-01-19 15:34:54 +11:00
/* 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
}
2012-05-17 23:21:11 +00:00
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 */
2018-07-26 11:07:56 +10:00
const char *RNA_property_identifier(const PropertyRNA *prop)
{
return rna_ensure_property_identifier(prop);
}
const char *RNA_property_description(PropertyRNA *prop)
{
Merge of the PyNodes branch (aka "custom nodes") into trunk. PyNodes opens up the node system in Blender to scripters and adds a number of UI-level improvements. === Dynamic node type registration === Node types can now be added at runtime, using the RNA registration mechanism from python. This enables addons such as render engines to create a complete user interface with nodes. Examples of how such nodes can be defined can be found in my personal wiki docs atm [1] and as a script template in release/scripts/templates_py/custom_nodes.py [2]. === Node group improvements === Each node editor now has a tree history of edited node groups, which allows opening and editing nested node groups. The node editor also supports pinning now, so that different spaces can be used to edit different node groups simultaneously. For more ramblings and rationale see (really old) blog post on code.blender.org [3]. The interface of node groups has been overhauled. Sockets of a node group are no longer displayed in columns on either side, but instead special input/output nodes are used to mirror group sockets inside a node tree. This solves the problem of long node lines in groups and allows more adaptable node layout. Internal sockets can be exposed from a group by either connecting to the extension sockets in input/output nodes (shown as empty circle) or by adding sockets from the node property bar in the "Interface" panel. Further details such as the socket name can also be changed there. [1] http://wiki.blender.org/index.php/User:Phonybone/Python_Nodes [2] http://projects.blender.org/scm/viewvc.php/trunk/blender/release/scripts/templates_py/custom_nodes.py?view=markup&root=bf-blender [3] http://code.blender.org/index.php/2012/01/improving-node-group-interface-editing/
2013-03-18 16:34:57 +00:00
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);
}
2013-03-07 02:44:55 +00:00
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;
}
}
Changes to Color Management After testing and feedback, I've decided to slightly modify the way color management works internally. While the previous method worked well for rendering, was a smaller transition and had some advantages over this new method, it was a bit more ambiguous, and was making things difficult for other areas such as compositing. This implementation now considers all color data (with only a couple of exceptions such as brush colors) to be stored in linear RGB color space, rather than sRGB as previously. This brings it in line with Nuke, which also operates this way, quite successfully. Color swatches, pickers, color ramp display are now gamma corrected to display gamma so you can see what you're doing, but the numbers themselves are considered linear. This makes understanding blending modes more clear (a 0.5 value on overlay will not change the result now) as well as making color swatches act more predictably in the compositor, however bringing over color values from applications like photoshop or gimp, that operate in a gamma space, will give identical results. This commit will convert over existing files saved by earlier 2.5 versions to work generally the same, though there may be some slight differences with things like textures. Now that we're set on changing other areas of shading, this won't be too disruptive overall. I've made a diagram explaining the pipeline here: http://mke3.net/blender/devel/2.5/25_linear_workflow_pipeline.png and some docs here: http://www.blender.org/development/release-logs/blender-250/color-management/
2009-12-02 07:56:34 +00:00
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;
2018-06-09 14:40:09 +02:00
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);
2012-10-14 13:21:40 +00:00
*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);
2012-08-07 19:49:38 +00:00
*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;
}
2018-07-02 11:47:00 +02:00
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;
}
2013-03-07 02:44:55 +00:00
bool RNA_property_enum_value(bContext *C, PointerRNA *ptr, PropertyRNA *prop, const char *identifier, int *r_value)
{
const EnumPropertyItem *item;
bool free;
2013-03-07 02:44:55 +00:00
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 {
2013-03-07 02:44:55 +00:00
found = false;
}
2009-12-25 23:59:12 +00:00
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) {
2013-03-07 02:44:55 +00:00
r_identifier[index++] = item->identifier;
}
}
2013-03-07 02:44:55 +00:00
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;
}
2013-03-07 02:44:55 +00:00
bool RNA_property_enum_identifier(bContext *C, PointerRNA *ptr, PropertyRNA *prop, const int value,
const char **identifier)
{
const EnumPropertyItem *item = NULL;
bool free;
2018-06-09 14:40:09 +02:00
RNA_property_enum_items(C, ptr, prop, &item, NULL, &free);
if (item) {
2013-03-07 02:44:55 +00:00
bool result;
result = RNA_enum_identifier(item, value, identifier);
if (free) {
MEM_freeN((void *)item);
}
return result;
}
2013-03-07 02:44:55 +00:00
return false;
}
2013-03-07 02:44:55 +00:00
bool RNA_property_enum_name(bContext *C, PointerRNA *ptr, PropertyRNA *prop, const int value, const char **name)
{
const EnumPropertyItem *item = NULL;
bool free;
2018-06-09 14:40:09 +02:00
RNA_property_enum_items(C, ptr, prop, &item, NULL, &free);
if (item) {
2013-03-07 02:44:55 +00:00
bool result;
result = RNA_enum_name(item, value, name);
if (free) {
MEM_freeN((void *)item);
}
2018-06-09 14:40:09 +02:00
return result;
}
2013-03-07 02:44:55 +00:00
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) {
2013-03-07 02:44:55 +00:00
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)
Merge of the PyNodes branch (aka "custom nodes") into trunk. PyNodes opens up the node system in Blender to scripters and adds a number of UI-level improvements. === Dynamic node type registration === Node types can now be added at runtime, using the RNA registration mechanism from python. This enables addons such as render engines to create a complete user interface with nodes. Examples of how such nodes can be defined can be found in my personal wiki docs atm [1] and as a script template in release/scripts/templates_py/custom_nodes.py [2]. === Node group improvements === Each node editor now has a tree history of edited node groups, which allows opening and editing nested node groups. The node editor also supports pinning now, so that different spaces can be used to edit different node groups simultaneously. For more ramblings and rationale see (really old) blog post on code.blender.org [3]. The interface of node groups has been overhauled. Sockets of a node group are no longer displayed in columns on either side, but instead special input/output nodes are used to mirror group sockets inside a node tree. This solves the problem of long node lines in groups and allows more adaptable node layout. Internal sockets can be exposed from a group by either connecting to the extension sockets in input/output nodes (shown as empty circle) or by adding sockets from the node property bar in the "Interface" panel. Further details such as the socket name can also be changed there. [1] http://wiki.blender.org/index.php/User:Phonybone/Python_Nodes [2] http://projects.blender.org/scm/viewvc.php/trunk/blender/release/scripts/templates_py/custom_nodes.py?view=markup&root=bf-blender [3] http://code.blender.org/index.php/2012/01/improving-node-group-interface-editing/
2013-03-18 16:34:57 +00:00
{
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)
Merge of the PyNodes branch (aka "custom nodes") into trunk. PyNodes opens up the node system in Blender to scripters and adds a number of UI-level improvements. === Dynamic node type registration === Node types can now be added at runtime, using the RNA registration mechanism from python. This enables addons such as render engines to create a complete user interface with nodes. Examples of how such nodes can be defined can be found in my personal wiki docs atm [1] and as a script template in release/scripts/templates_py/custom_nodes.py [2]. === Node group improvements === Each node editor now has a tree history of edited node groups, which allows opening and editing nested node groups. The node editor also supports pinning now, so that different spaces can be used to edit different node groups simultaneously. For more ramblings and rationale see (really old) blog post on code.blender.org [3]. The interface of node groups has been overhauled. Sockets of a node group are no longer displayed in columns on either side, but instead special input/output nodes are used to mirror group sockets inside a node tree. This solves the problem of long node lines in groups and allows more adaptable node layout. Internal sockets can be exposed from a group by either connecting to the extension sockets in input/output nodes (shown as empty circle) or by adding sockets from the node property bar in the "Interface" panel. Further details such as the socket name can also be changed there. [1] http://wiki.blender.org/index.php/User:Phonybone/Python_Nodes [2] http://projects.blender.org/scm/viewvc.php/trunk/blender/release/scripts/templates_py/custom_nodes.py?view=markup&root=bf-blender [3] http://code.blender.org/index.php/2012/01/improving-node-group-interface-editing/
2013-03-18 16:34:57 +00:00
{
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;
}
2013-03-07 02:44:55 +00:00
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);
}
2013-03-07 02:44:55 +00:00
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;
2013-03-07 02:44:55 +00:00
return (flag & PROP_EDITABLE) != 0;
}
/* same as RNA_property_editable(), except this checks individual items in an array */
2013-03-07 02:44:55 +00:00
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));
}
2013-03-07 02:44:55 +00:00
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))
2013-03-07 02:44:55 +00:00
return false;
2018-06-09 14:40:09 +02:00
prop = rna_ensure_property(prop);
if (!(prop->flag & PROP_ANIMATABLE))
2013-03-07 02:44:55 +00:00
return false;
2013-03-07 02:44:55 +00:00
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;
}
2013-03-07 02:44:55 +00:00
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
2012-03-18 07:38:51 +00:00
* its slow so don't call in a loop */
2013-03-07 02:44:55 +00:00
bool RNA_property_path_from_ID_check(PointerRNA *ptr, PropertyRNA *prop)
{
char *path = RNA_path_from_ID_to_property(ptr, prop);
2013-03-07 02:44:55 +00:00
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
2012-03-09 18:28:30 +00:00
* 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
2012-03-18 07:38:51 +00:00
* but this isn't likely to be a performance problem. */
2013-03-07 02:44:55 +00:00
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,
2012-06-06 14:48:39 +00:00
* 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;
2018-06-09 14:40:09 +02:00
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;
2018-06-09 14:40:09 +02:00
/* sanity check */
if (NULL == ptr)
return;
2018-06-09 14:40:09 +02:00
prop = rna_ensure_property(prop);
2018-06-09 14:40:09 +02:00
/* 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;
2018-06-09 14:40:09 +02:00
/* 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);
2018-06-09 14:40:09 +02:00
/* copy pointer */
RNA_pointer_create(ptr->id.data, ptr->type, ptr->data, &uce->ptr);
}
2018-06-09 14:40:09 +02:00
/* 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;
2018-06-09 14:40:09 +02:00
/* TODO: should we check that bmain and scene are valid? The above stuff doesn't! */
2018-06-09 14:40:09 +02:00
/* execute the cached updates */
for (uce = rna_updates_cache.first; uce; uce = uce->next) {
LinkData *ld;
2018-06-09 14:40:09 +02:00
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;
2018-06-09 14:40:09 +02:00
for (uce = rna_updates_cache.first; uce; uce = ucn) {
ucn = uce->next;
2018-06-09 14:40:09 +02:00
/* free L2 cache */
BLI_freelistN(&uce->L2Funcs);
2018-06-09 14:40:09 +02:00
/* 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);
2013-03-07 02:44:55 +00:00
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);
2013-03-07 02:44:55 +00:00
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);
2013-03-07 02:44:55 +00:00
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);
2013-03-07 02:44:55 +00:00
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);
2013-03-07 02:44:55 +00:00
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);
2013-03-07 02:44:55 +00:00
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);
2013-03-07 02:44:55 +00:00
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);
2018-06-09 14:40:09 +02:00
BLI_assert(RNA_property_type(prop) == PROP_BOOLEAN);
2013-03-07 02:44:55 +00:00
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);
2013-03-07 02:44:55 +00:00
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);
2013-03-07 02:44:55 +00:00
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);
2013-03-07 02:44:55 +00:00
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);
2013-03-07 02:44:55 +00:00
BLI_assert(RNA_property_array_check(prop) != false);
if ((idprop = rna_idproperty_check(&prop, ptr))) {
2014-04-07 19:51:43 +10:00
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);
}
2011-06-24 03:30:50 +00:00
void RNA_property_int_get_array_range(PointerRNA *ptr, PropertyRNA *prop, int values[2])
{
const int array_len = RNA_property_array_length(ptr, prop);
2011-06-24 03:30:50 +00:00
if (array_len <= 0) {
values[0] = 0;
values[1] = 0;
2011-06-24 03:30:50 +00:00
}
else if (array_len == 1) {
RNA_property_int_get_array(ptr, prop, values);
values[1] = values[0];
2011-06-24 03:30:50 +00:00
}
else {
int arr_stack[32];
int *arr;
int i;
if (array_len > 32) {
2015-02-19 06:57:45 +11:00
arr = MEM_mallocN(sizeof(int) * array_len, __func__);
2011-06-24 03:30:50 +00:00
}
else {
arr = arr_stack;
2011-06-24 03:30:50 +00:00
}
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]);
2011-06-24 03:30:50 +00:00
}
if (arr != arr_stack) {
2011-06-24 03:30:50 +00:00
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);
2013-03-07 02:44:55 +00:00
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);
2013-03-07 02:44:55 +00:00
BLI_assert(RNA_property_array_check(prop) != false);
if ((idprop = rna_idproperty_check(&prop, ptr))) {
2014-04-07 19:51:43 +10:00
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);
2013-03-07 02:44:55 +00:00
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);
}
}
2011-05-31 02:14:25 +00:00
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);
}
}
2011-05-31 02:14:25 +00:00
void RNA_property_int_get_default_array(PointerRNA *UNUSED(ptr), PropertyRNA *prop, int *values)
{
IntPropertyRNA *iprop = (IntPropertyRNA *)rna_ensure_property(prop);
2018-06-09 14:40:09 +02:00
BLI_assert(RNA_property_type(prop) == PROP_INT);
2013-03-07 02:44:55 +00:00
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);
2013-03-07 02:44:55 +00:00
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);
2013-03-07 02:44:55 +00:00
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);
2013-03-07 02:44:55 +00:00
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);
2013-03-07 02:44:55 +00:00
BLI_assert(RNA_property_array_check(prop) != false);
if ((idprop = rna_idproperty_check(&prop, ptr))) {
2014-04-07 19:51:43 +10:00
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);
}
2011-06-24 03:30:50 +00:00
void RNA_property_float_get_array_range(PointerRNA *ptr, PropertyRNA *prop, float values[2])
{
const int array_len = RNA_property_array_length(ptr, prop);
2011-06-24 03:30:50 +00:00
if (array_len <= 0) {
values[0] = 0.0f;
values[1] = 0.0f;
2011-06-24 03:30:50 +00:00
}
else if (array_len == 1) {
RNA_property_float_get_array(ptr, prop, values);
values[1] = values[0];
2011-06-24 03:30:50 +00:00
}
else {
float arr_stack[32];
float *arr;
int i;
if (array_len > 32) {
2015-02-19 06:57:45 +11:00
arr = MEM_mallocN(sizeof(float) * array_len, __func__);
2011-06-24 03:30:50 +00:00
}
else {
arr = arr_stack;
2011-06-24 03:30:50 +00:00
}
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]);
2011-06-24 03:30:50 +00:00
}
if (arr != arr_stack) {
2011-06-24 03:30:50 +00:00
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);
2013-03-07 02:44:55 +00:00
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);
2013-03-07 02:44:55 +00:00
BLI_assert(RNA_property_array_check(prop) != false);
if ((idprop = rna_idproperty_check(&prop, ptr))) {
2014-04-07 19:51:43 +10:00
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);
2013-03-07 02:44:55 +00:00
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);
}
}
2011-05-31 02:14:25 +00:00
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);
2013-03-07 02:44:55 +00:00
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);
}
}
2011-05-31 02:14:25 +00:00
void RNA_property_float_get_default_array(PointerRNA *UNUSED(ptr), PropertyRNA *prop, float *values)
{
FloatPropertyRNA *fprop = (FloatPropertyRNA *)rna_ensure_property(prop);
2018-06-09 14:40:09 +02:00
BLI_assert(RNA_property_type(prop) == PROP_FLOAT);
2013-03-07 02:44:55 +00:00
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);
2013-03-07 02:44:55 +00:00
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));
}
}
}
2011-05-31 02:14:25 +00:00
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 */
2011-05-31 02:14:25 +00:00
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));
}
}
2011-05-31 02:14:25 +00:00
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
2012-03-09 18:28:30 +00:00
* 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))
{
2012-07-29 00:20:28 +00:00
/*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);
2018-06-09 14:40:09 +02:00
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) {
2013-05-14 06:58:35 +00:00
internal->ptr += internal->itemsize * (num - 1);
iter->valid = (internal->ptr < internal->endptr);
2013-05-11 01:06:01 +00:00
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;
2012-07-29 00:20:28 +00:00
/* 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(&params, ptr, cprop->add);
RNA_function_call(NULL, NULL, ptr, cprop->add, &params);
RNA_parameter_list_free(&params);
}
}
2016-07-15 02:36:21 +10:00
#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));
}
}
2013-03-07 02:44:55 +00:00
bool RNA_property_collection_remove(PointerRNA *ptr, PropertyRNA *prop, int key)
{
IDProperty *idprop;
2012-07-29 00:20:28 +00:00
/* 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);
}
2013-03-07 02:44:55 +00:00
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(&params, ptr, cprop->remove);
RNA_function_call(NULL, NULL, ptr, cprop->remove, &params);
RNA_parameter_list_free(&params);
}
2013-03-07 02:44:55 +00:00
return false;
}
2016-07-15 02:36:21 +10:00
#if 0
else {
printf("%s %s.%s: only supported for id properties.\n", __func__, ptr->type->identifier, prop->identifier);
}
#endif
#endif
2013-03-07 02:44:55 +00:00
return false;
}
2013-03-07 02:44:55 +00:00
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));
}
2013-03-07 02:44:55 +00:00
return true;
}
else if (prop->flag & PROP_IDPROPERTY) {
return true;
}
2013-03-07 02:44:55 +00:00
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;
2018-06-09 14:40:09 +02:00
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);
2018-06-09 14:40:09 +02:00
/* 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;
}
2013-03-07 02:44:55 +00:00
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) { \
2012-07-29 00:20:28 +00:00
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; \
2012-07-29 00:20:28 +00:00
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; \
} \
2012-05-27 20:13:59 +00:00
} (void)0
#define RAW_SET(dtype, raw, a, var) \
{ \
switch (raw.type) { \
2012-07-29 00:20:28 +00:00
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; \
2012-07-29 00:20:28 +00:00
case PROP_RAW_FLOAT: ((float *)raw.array)[a] = (float)var; break; \
case PROP_RAW_DOUBLE: ((double *)raw.array)[a] = (double)var; break; \
default: break; \
} \
2012-05-27 20:13:59 +00:00
} (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;
2013-02-03 10:28:28 +00:00
if (dim == 0)
return 0;
2013-02-03 10:28:28 +00:00
for (size = 1, i = 0; i < dim; i++)
size *= len[i];
2018-06-09 14:40:09 +02:00
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 */
2013-02-03 10:28:28 +00:00
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;
}
2018-06-09 14:40:09 +02:00
/* 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) {
2012-09-08 08:59:47 +00:00
case PROP_BOOLEAN:
{
int b;
RAW_GET(bool, b, in, a);
RNA_property_boolean_set(&itemptr, iprop, b);
break;
}
2012-09-08 08:59:47 +00:00
case PROP_INT:
{
int i;
RAW_GET(int, i, in, a);
RNA_property_int_set(&itemptr, iprop, i);
break;
}
2012-09-08 08:59:47 +00:00
case PROP_FLOAT:
{
float f;
RAW_GET(float, f, in, a);
RNA_property_float_set(&itemptr, iprop, f);
break;
}
default:
break;
}
}
else {
switch (itemtype) {
2012-09-08 08:59:47 +00:00
case PROP_BOOLEAN:
{
int b = RNA_property_boolean_get(&itemptr, iprop);
RAW_SET(bool, in, a, b);
break;
}
2012-09-08 08:59:47 +00:00
case PROP_INT:
{
int i = RNA_property_int_get(&itemptr, iprop);
RAW_SET(int, in, a, i);
break;
}
2012-09-08 08:59:47 +00:00
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) {
2015-02-19 06:57:45 +11:00
tmparray = MEM_callocN(sizeof(float) * itemlen, "RNA tmparray");
tmplen = itemlen;
}
/* handle conversions */
if (set) {
switch (itemtype) {
2012-09-08 08:59:47 +00:00
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;
}
2012-09-08 08:59:47 +00:00
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;
}
2012-09-08 08:59:47 +00:00
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) {
2012-09-08 08:59:47 +00:00
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;
}
2012-09-08 08:59:47 +00:00
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;
}
2012-09-08 08:59:47 +00:00
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) {
2012-09-08 08:59:47 +00:00
case PROP_BOOLEAN:
{
RNA_property_boolean_set_array(&itemptr, iprop, &((bool *)in.array)[a]);
a += itemlen;
break;
}
2012-09-08 08:59:47 +00:00
case PROP_INT:
{
RNA_property_int_set_array(&itemptr, iprop, &((int *)in.array)[a]);
a += itemlen;
break;
}
2012-09-08 08:59:47 +00:00
case PROP_FLOAT:
{
RNA_property_float_set_array(&itemptr, iprop, &((float *)in.array)[a]);
a += itemlen;
break;
}
default:
break;
}
}
else {
switch (itemtype) {
2012-09-08 08:59:47 +00:00
case PROP_BOOLEAN:
{
RNA_property_boolean_get_array(&itemptr, iprop, &((bool *)in.array)[a]);
a += itemlen;
break;
}
2012-09-08 08:59:47 +00:00
case PROP_INT:
{
RNA_property_int_get_array(&itemptr, iprop, &((int *)in.array)[a]);
a += itemlen;
break;
}
2012-09-08 08:59:47 +00:00
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;
2018-06-09 14:40:09 +02:00
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;
2018-06-09 14:40:09 +02:00
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++;
}
2018-06-09 14:40:09 +02:00
/* 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++;
}
}
2018-06-09 14:40:09 +02:00
/* empty, return */
if (len == 0)
return NULL;
2018-06-09 14:40:09 +02:00
/* 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++) {
2012-10-14 13:08:19 +00:00
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;
2018-06-09 14:40:09 +02:00
*r_nextptr = *ptr;
/* end of path, ok */
if (!(**path))
return true;
2018-06-09 14:40:09 +02:00
if (**path == '[') {
char *token;
/* resolve the lookup with [] brackets */
token = rna_path_token(path, fixedbuf, sizeof(fixedbuf), 1);
2018-06-09 14:40:09 +02:00
if (!token)
return false;
2018-06-09 14:40:09 +02:00
/* 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;
}
}
2018-06-09 14:40:09 +02:00
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;
}
}
2018-06-09 14:40:09 +02:00
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;
2018-06-09 14:40:09 +02:00
*r_index = -1;
2018-06-09 14:40:09 +02:00
/* end of path, ok */
if (!(**path))
return true;
2018-06-09 14:40:09 +02:00
for (i = 0; i < dim; i++) {
int temp_index = -1;
char *token;
2018-06-09 14:40:09 +02:00
/* multi index resolve */
if (**path == '[') {
token = rna_path_token(path, fixedbuf, sizeof(fixedbuf), 1);
2018-06-09 14:40:09 +02:00
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);
2018-06-09 14:40:09 +02:00
if (temp_index == 0 && (token[0] != '0' || token[1] != '\0')) {
if (token != fixedbuf) {
MEM_freeN(token);
}
2018-06-09 14:40:09 +02:00
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;
}
2018-06-09 14:40:09 +02:00
if (token != fixedbuf) {
MEM_freeN(token);
}
2018-06-09 14:40:09 +02:00
/* out of range */
if (temp_index < 0 || temp_index >= len[i])
return false;
2018-06-09 14:40:09 +02:00
index_arr[i] = temp_index;
/* end multi index resolve */
}
2018-06-09 14:40:09 +02:00
/* arrays always contain numbers so further values are not valid */
if (**path)
return false;
2018-06-09 14:40:09 +02:00
/* flatten index over all dimensions */
{
int totdim = 1;
int flat_index = 0;
2018-06-09 14:40:09 +02:00
for (i = dim - 1; i >= 0; i--) {
flat_index += index_arr[i] * totdim;
totdim *= len[i];
}
2018-06-09 14:40:09 +02:00
*r_index = flat_index;
}
return true;
}
/**
* Generic rna path parser.
*
* \note All parameters besides \a ptr and \a path are optional.
*
2018-12-12 12:55:20 +11:00
* \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.
2018-12-12 12:55:20 +11:00
* \param r_elements: A list of \a PropertyElemRNA items
* (pairs of \a PointerRNA, \a PropertyRNA that represent the whole given \a path).
2018-12-12 12:55:20 +11:00
* \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);
}
2018-06-09 14:40:09 +02:00
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;
}
2018-06-09 14:40:09 +02:00
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.
2018-12-12 12:55:20 +11:00
* \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.
2018-12-12 12:55:20 +11:00
* \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);
}
2011-05-31 02:14:25 +00:00
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;
2018-06-09 14:40:09 +02:00
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(&current, 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(&current, fixedbuf, sizeof(fixedbuf), 1);
if (token && token != fixedbuf)
MEM_freeN(token);
2018-06-09 14:40:09 +02:00
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;
2011-01-09 15:12:08 +00:00
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;
2011-01-09 15:12:08 +00:00
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;
2018-06-09 14:40:09 +02:00
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;
2018-06-09 14:40:09 +02:00
/* 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);
2018-06-09 14:40:09 +02:00
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;
}
2018-06-09 14:40:09 +02:00
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]);
}
}
/**
2016-06-29 20:37:54 +10:00
* \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;
2018-06-09 14:40:09 +02:00
/* 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);
}
2012-11-25 15:05:17 +00:00
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;
2015-09-11 03:35:38 +10:00
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;
}
2015-09-11 03:35:38 +10:00
if ((index == -1) || (RNA_property_array_check(prop) == false)) {
2015-09-11 03:35:38 +10:00
ret = BLI_sprintfN("%s%s%s",
id_path, data_delim, data_path);
}
else {
2015-09-11 03:35:38 +10:00
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)) {
2015-02-17 13:09:21 +11:00
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);
}
}
2013-03-07 02:44:55 +00:00
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;
2014-11-16 20:24:58 +13:00
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);
2013-03-07 02:44:55 +00:00
return false;
}
else {
printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name);
2013-03-07 02:44:55 +00:00
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;
}
}
2013-03-07 02:44:55 +00:00
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);
2013-03-07 02:44:55 +00:00
return ((idprop != NULL) && (use_ghost == false || !(idprop->flag & IDP_FLAG_GHOST)));
}
else {
2013-03-07 02:44:55 +00:00
return true;
}
}
2013-03-07 02:44:55 +00:00
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 {
2013-03-07 02:44:55 +00:00
return true;
}
}
Merge of the PyNodes branch (aka "custom nodes") into trunk. PyNodes opens up the node system in Blender to scripters and adds a number of UI-level improvements. === Dynamic node type registration === Node types can now be added at runtime, using the RNA registration mechanism from python. This enables addons such as render engines to create a complete user interface with nodes. Examples of how such nodes can be defined can be found in my personal wiki docs atm [1] and as a script template in release/scripts/templates_py/custom_nodes.py [2]. === Node group improvements === Each node editor now has a tree history of edited node groups, which allows opening and editing nested node groups. The node editor also supports pinning now, so that different spaces can be used to edit different node groups simultaneously. For more ramblings and rationale see (really old) blog post on code.blender.org [3]. The interface of node groups has been overhauled. Sockets of a node group are no longer displayed in columns on either side, but instead special input/output nodes are used to mirror group sockets inside a node tree. This solves the problem of long node lines in groups and allows more adaptable node layout. Internal sockets can be exposed from a group by either connecting to the extension sockets in input/output nodes (shown as empty circle) or by adding sockets from the node property bar in the "Interface" panel. Further details such as the socket name can also be changed there. [1] http://wiki.blender.org/index.php/User:Phonybone/Python_Nodes [2] http://projects.blender.org/scm/viewvc.php/trunk/blender/release/scripts/templates_py/custom_nodes.py?view=markup&root=bf-blender [3] http://code.blender.org/index.php/2012/01/improving-node-group-interface-editing/
2013-03-18 16:34:57 +00:00
void RNA_property_unset(PointerRNA *ptr, PropertyRNA *prop)
{
prop = rna_ensure_property(prop);
Merge of the PyNodes branch (aka "custom nodes") into trunk. PyNodes opens up the node system in Blender to scripters and adds a number of UI-level improvements. === Dynamic node type registration === Node types can now be added at runtime, using the RNA registration mechanism from python. This enables addons such as render engines to create a complete user interface with nodes. Examples of how such nodes can be defined can be found in my personal wiki docs atm [1] and as a script template in release/scripts/templates_py/custom_nodes.py [2]. === Node group improvements === Each node editor now has a tree history of edited node groups, which allows opening and editing nested node groups. The node editor also supports pinning now, so that different spaces can be used to edit different node groups simultaneously. For more ramblings and rationale see (really old) blog post on code.blender.org [3]. The interface of node groups has been overhauled. Sockets of a node group are no longer displayed in columns on either side, but instead special input/output nodes are used to mirror group sockets inside a node tree. This solves the problem of long node lines in groups and allows more adaptable node layout. Internal sockets can be exposed from a group by either connecting to the extension sockets in input/output nodes (shown as empty circle) or by adding sockets from the node property bar in the "Interface" panel. Further details such as the socket name can also be changed there. [1] http://wiki.blender.org/index.php/User:Phonybone/Python_Nodes [2] http://projects.blender.org/scm/viewvc.php/trunk/blender/release/scripts/templates_py/custom_nodes.py?view=markup&root=bf-blender [3] http://code.blender.org/index.php/2012/01/improving-node-group-interface-editing/
2013-03-18 16:34:57 +00:00
if (prop->flag & PROP_IDPROPERTY) {
rna_idproperty_free(ptr, prop->identifier);
}
}
2013-03-07 02:44:55 +00:00
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;
}
}
2013-03-07 02:44:55 +00:00
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;
}
}
Merge of the PyNodes branch (aka "custom nodes") into trunk. PyNodes opens up the node system in Blender to scripters and adds a number of UI-level improvements. === Dynamic node type registration === Node types can now be added at runtime, using the RNA registration mechanism from python. This enables addons such as render engines to create a complete user interface with nodes. Examples of how such nodes can be defined can be found in my personal wiki docs atm [1] and as a script template in release/scripts/templates_py/custom_nodes.py [2]. === Node group improvements === Each node editor now has a tree history of edited node groups, which allows opening and editing nested node groups. The node editor also supports pinning now, so that different spaces can be used to edit different node groups simultaneously. For more ramblings and rationale see (really old) blog post on code.blender.org [3]. The interface of node groups has been overhauled. Sockets of a node group are no longer displayed in columns on either side, but instead special input/output nodes are used to mirror group sockets inside a node tree. This solves the problem of long node lines in groups and allows more adaptable node layout. Internal sockets can be exposed from a group by either connecting to the extension sockets in input/output nodes (shown as empty circle) or by adding sockets from the node property bar in the "Interface" panel. Further details such as the socket name can also be changed there. [1] http://wiki.blender.org/index.php/User:Phonybone/Python_Nodes [2] http://projects.blender.org/scm/viewvc.php/trunk/blender/release/scripts/templates_py/custom_nodes.py?view=markup&root=bf-blender [3] http://code.blender.org/index.php/2012/01/improving-node-group-interface-editing/
2013-03-18 16:34:57 +00:00
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;
2018-06-09 14:40:09 +02:00
const char *propname;
int first_time = 1;
2018-06-09 14:40:09 +02:00
BLI_dynstr_append(dynstr, "{");
2018-06-09 14:40:09 +02:00
RNA_STRUCT_BEGIN (ptr, prop)
{
propname = RNA_property_identifier(prop);
2018-06-09 14:40:09 +02:00
if (STREQ(propname, "rna_type"))
continue;
2018-06-09 14:40:09 +02:00
if (first_time == 0)
BLI_dynstr_append(dynstr, ", ");
first_time = 0;
2018-06-09 14:40:09 +02:00
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, "}");
2018-06-09 14:40:09 +02:00
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;
2019-02-26 14:31:14 +11:00
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;
2018-06-09 14:40:09 +02:00
/* 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);
}
2018-06-09 14:40:09 +02:00
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)
Merge of the PyNodes branch (aka "custom nodes") into trunk. PyNodes opens up the node system in Blender to scripters and adds a number of UI-level improvements. === Dynamic node type registration === Node types can now be added at runtime, using the RNA registration mechanism from python. This enables addons such as render engines to create a complete user interface with nodes. Examples of how such nodes can be defined can be found in my personal wiki docs atm [1] and as a script template in release/scripts/templates_py/custom_nodes.py [2]. === Node group improvements === Each node editor now has a tree history of edited node groups, which allows opening and editing nested node groups. The node editor also supports pinning now, so that different spaces can be used to edit different node groups simultaneously. For more ramblings and rationale see (really old) blog post on code.blender.org [3]. The interface of node groups has been overhauled. Sockets of a node group are no longer displayed in columns on either side, but instead special input/output nodes are used to mirror group sockets inside a node tree. This solves the problem of long node lines in groups and allows more adaptable node layout. Internal sockets can be exposed from a group by either connecting to the extension sockets in input/output nodes (shown as empty circle) or by adding sockets from the node property bar in the "Interface" panel. Further details such as the socket name can also be changed there. [1] http://wiki.blender.org/index.php/User:Phonybone/Python_Nodes [2] http://projects.blender.org/scm/viewvc.php/trunk/blender/release/scripts/templates_py/custom_nodes.py?view=markup&root=bf-blender [3] http://code.blender.org/index.php/2012/01/improving-node-group-interface-editing/
2013-03-18 16:34:57 +00:00
{
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;
}
2011-05-31 02:14:25 +00:00
PropertyRNA *RNA_function_get_parameter(PointerRNA *UNUSED(ptr), FunctionRNA *func, int index)
{
return BLI_findlink(&func->cont.properties, index);
}
2011-05-31 02:14:25 +00:00
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;
}
2012-05-17 23:21:11 +00:00
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;
}
2011-05-31 02:14:25 +00:00
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;
}
2018-06-09 14:40:09 +02:00
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;
2012-09-08 08:59:47 +00:00
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)
{
2011-08-15 03:41:31 +00:00
/* 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);
}
}
2011-05-31 02:14:25 +00:00
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;
}
2018-06-09 14:40:09 +02:00
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;
}
2017-06-12 13:35:00 +10:00
*((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);
2018-06-09 14:40:09 +02:00
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;
}
}
2009-04-11 13:24:02 +00:00
if (err != 0)
2009-04-11 13:24:02 +00:00
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 *);
2018-10-11 08:49:28 +11:00
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(
2017-11-29 14:09:49 +11:00
const char *text, const char *text_ctxt, StructRNA *type, PropertyRNA *prop, int translate)
{
return rna_translate_ui_text(text, text_ctxt, type, prop, translate);
}
2013-03-07 02:44:55 +00:00
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);
2018-06-09 14:40:09 +02:00
/* 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");
2018-06-09 14:40:09 +02:00
RNA_property_boolean_get_default_array(ptr, prop, tmparray);
RNA_property_boolean_set_array(ptr, prop, tmparray);
2018-06-09 14:40:09 +02:00
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);
}
2013-03-07 02:44:55 +00:00
return true;
case PROP_INT:
if (len) {
if (index == -1) {
int *tmparray = MEM_callocN(sizeof(int) * len, "reset_defaults - int");
2018-06-09 14:40:09 +02:00
RNA_property_int_get_default_array(ptr, prop, tmparray);
RNA_property_int_set_array(ptr, prop, tmparray);
2018-06-09 14:40:09 +02:00
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);
}
2013-03-07 02:44:55 +00:00
return true;
case PROP_FLOAT:
if (len) {
if (index == -1) {
float *tmparray = MEM_callocN(sizeof(float) * len, "reset_defaults - float");
2018-06-09 14:40:09 +02:00
RNA_property_float_get_default_array(ptr, prop, tmparray);
RNA_property_float_set_array(ptr, prop, tmparray);
2018-06-09 14:40:09 +02:00
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);
}
2013-03-07 02:44:55 +00:00
return true;
case PROP_ENUM:
{
int value = RNA_property_enum_get_default(ptr, prop);
RNA_property_enum_set(ptr, prop, value);
2013-03-07 02:44:55 +00:00
return true;
}
2018-06-09 14:40:09 +02:00
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);
2013-03-07 02:44:55 +00:00
return true;
}
2018-06-09 14:40:09 +02:00
case PROP_POINTER:
{
PointerRNA value = RNA_property_pointer_get_default(ptr, prop);
RNA_property_pointer_set(ptr, prop, value);
2013-03-07 02:44:55 +00:00
return true;
}
2018-06-09 14:40:09 +02:00
default:
/* FIXME: are there still any cases that haven't been handled? comment out "default" block to check :) */
2013-03-07 02:44:55 +00:00
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)) {
2013-03-07 02:44:55 +00:00
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).
2018-03-15 09:44:03 +01:00
*
2018-03-16 14:10:04 +01:00
* \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,
2018-03-16 14:10:04 +01:00
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;
}
Fix #36226, Select Linked works not in touch with Prefs. When setting keymap properties to values equalling the RNA default, they will get "unset" and automatic operator behavior is used. There is no way to explicitly set the default value as a user. 1) To allow distinguishing uninitialized (not set) properties in the keymap items, a few changes to the RNA struct comparison function are needed: Instead of allowing only strict/non-strict comparison of 2 properties A and B in a struct, this now has 3 modes: * STRICT: compare only the actual property values (same as 'strict' before) * UNSET_MATCH_ANY: if either A or B is unset, consider them a match (same as non-strict before) * UNSET_MATCH_NONE: if one property is set and the other not, consider them a mismatch. The new UNSET_MATCH_NONE mode is useful for keymaps, because it allows keeping user-defined property values in the keymap even if they match the default property value (see wm_keymap_diff function in wm_keymap.c) 2) A new operator is added for unsetting ID properties in the RMB context menu and in user preferences next to keymap properties. This only works on ID properties and deletes the ID property storage, so that the default value is used. In the user preferences for keymaps the properties are shown in an inactive layout to indicate that the default value is used (which some operators such as the "select linked" op from the report use to trigger automatic behavior). When the user sets a property it gets set and stays that way until explicitly "unset" using the new operator.
2013-09-20 09:10:17 +00:00
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;
}
Fix #36226, Select Linked works not in touch with Prefs. When setting keymap properties to values equalling the RNA default, they will get "unset" and automatic operator behavior is used. There is no way to explicitly set the default value as a user. 1) To allow distinguishing uninitialized (not set) properties in the keymap items, a few changes to the RNA struct comparison function are needed: Instead of allowing only strict/non-strict comparison of 2 properties A and B in a struct, this now has 3 modes: * STRICT: compare only the actual property values (same as 'strict' before) * UNSET_MATCH_ANY: if either A or B is unset, consider them a match (same as non-strict before) * UNSET_MATCH_NONE: if one property is set and the other not, consider them a mismatch. The new UNSET_MATCH_NONE mode is useful for keymaps, because it allows keeping user-defined property values in the keymap even if they match the default property value (see wm_keymap_diff function in wm_keymap.c) 2) A new operator is added for unsetting ID properties in the RMB context menu and in user preferences next to keymap properties. This only works on ID properties and deletes the ID property storage, so that the default value is used. In the user preferences for keymaps the properties are shown in an inactive layout to indicate that the default value is used (which some operators such as the "select linked" op from the report use to trigger automatic behavior). When the user sets a property it gets set and stays that way until explicitly "unset" using the new operator.
2013-09-20 09:10:17 +00:00
}
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;
}
Fix #36226, Select Linked works not in touch with Prefs. When setting keymap properties to values equalling the RNA default, they will get "unset" and automatic operator behavior is used. There is no way to explicitly set the default value as a user. 1) To allow distinguishing uninitialized (not set) properties in the keymap items, a few changes to the RNA struct comparison function are needed: Instead of allowing only strict/non-strict comparison of 2 properties A and B in a struct, this now has 3 modes: * STRICT: compare only the actual property values (same as 'strict' before) * UNSET_MATCH_ANY: if either A or B is unset, consider them a match (same as non-strict before) * UNSET_MATCH_NONE: if one property is set and the other not, consider them a mismatch. The new UNSET_MATCH_NONE mode is useful for keymaps, because it allows keeping user-defined property values in the keymap even if they match the default property value (see wm_keymap_diff function in wm_keymap.c) 2) A new operator is added for unsetting ID properties in the RMB context menu and in user preferences next to keymap properties. This only works on ID properties and deletes the ID property storage, so that the default value is used. In the user preferences for keymaps the properties are shown in an inactive layout to indicate that the default value is used (which some operators such as the "select linked" op from the report use to trigger automatic behavior). When the user sets a property it gets set and stays that way until explicitly "unset" using the new operator.
2013-09-20 09:10:17 +00:00
}
/* 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))
2013-01-15 23:45:41 +00:00
{
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.
*
2018-12-12 12:55:20 +11:00
* \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, ...) \
2018-04-23 16:13:50 +02:00
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 \
2018-04-23 16:13:50 +02:00
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;
}