Cleanup: Move RNA path functions into own C++ file
Adds `rna_path.cc` and `RNA_path.h`. `rna_access.c` is a quite big file, which makes it rather hard and inconvenient to navigate. RNA path functions form a nicely coherent unit that can stand well on it's own, so it makes sense to split them off to mitigate the problem. Moreover, I was looking into refactoring the quite convoluted/overloaded `rna_path_parse()`, and found that some C++ features may help greatly with that. So having that code compile in C++ would be helpful to attempt that. Differential Revision: https://developer.blender.org/D15540 Reviewed by: Brecht Van Lommel, Campbell Barton, Bastien Montagne
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
#include "MEM_guardedalloc.h"
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_blender_cpp.h"
|
||||
#include "RNA_path.h"
|
||||
#include "RNA_types.h"
|
||||
|
||||
#include "blender/id_map.h"
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
#include "BIK_api.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_path.h"
|
||||
#include "RNA_prototypes.h"
|
||||
|
||||
#include "BLO_read_write.h"
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
#include "BLO_read_write.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_path.h"
|
||||
|
||||
#include "CLG_log.h"
|
||||
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
#include "DEG_depsgraph_query.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_path.h"
|
||||
#include "RNA_prototypes.h"
|
||||
|
||||
#include "BLO_read_write.h"
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
#include "BLO_read_write.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_path.h"
|
||||
|
||||
#include "CLG_log.h"
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "BKE_object.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_path.h"
|
||||
|
||||
#include "atomic_ops.h"
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
#include "BKE_scene.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_path.h"
|
||||
#include "RNA_prototypes.h"
|
||||
|
||||
#include "BLO_read_write.h"
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
#include "PIL_time.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_path.h"
|
||||
#include "RNA_prototypes.h"
|
||||
#include "RNA_types.h"
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
|
||||
#include "BKE_animsys.h"
|
||||
|
||||
#include "RNA_path.h"
|
||||
|
||||
namespace blender::deg {
|
||||
|
||||
/* Animated property storage. */
|
||||
|
||||
@@ -93,6 +93,7 @@
|
||||
#include "BKE_world.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_path.h"
|
||||
#include "RNA_prototypes.h"
|
||||
#include "RNA_types.h"
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "DNA_ID.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_path.h"
|
||||
#include "RNA_types.h"
|
||||
|
||||
#include "BLI_string.h"
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_path.h"
|
||||
#include "RNA_prototypes.h"
|
||||
|
||||
#include "DEG_depsgraph.h"
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "BKE_duplilist.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_path.h"
|
||||
|
||||
#include "BLI_bitmap.h"
|
||||
#include "BLI_memblock.h"
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
#include "DNA_world_types.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_path.h"
|
||||
#include "RNA_prototypes.h"
|
||||
|
||||
#include "BKE_anim_data.h"
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "DEG_depsgraph.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_path.h"
|
||||
|
||||
#include "SEQ_sequencer.h"
|
||||
#include "SEQ_utils.h"
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
#include "ED_keyframes_keylist.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_path.h"
|
||||
|
||||
#include "UI_interface.h"
|
||||
#include "UI_resources.h"
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "DNA_anim_types.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_path.h"
|
||||
#include "RNA_prototypes.h"
|
||||
|
||||
#include "ED_anim_api.h"
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_define.h"
|
||||
#include "RNA_path.h"
|
||||
#include "RNA_prototypes.h"
|
||||
|
||||
#include "anim_intern.h"
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_enum_types.h"
|
||||
#include "RNA_path.h"
|
||||
|
||||
#include "ED_anim_api.h"
|
||||
#include "ED_keyframes_edit.h"
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_define.h"
|
||||
#include "RNA_enum_types.h"
|
||||
#include "RNA_path.h"
|
||||
#include "RNA_prototypes.h"
|
||||
|
||||
#include "anim_intern.h"
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_define.h"
|
||||
#include "RNA_enum_types.h"
|
||||
#include "RNA_path.h"
|
||||
|
||||
#include "anim_intern.h"
|
||||
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_define.h"
|
||||
#include "RNA_path.h"
|
||||
#include "RNA_prototypes.h"
|
||||
|
||||
#include "WM_api.h"
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "DEG_depsgraph.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_path.h"
|
||||
#include "RNA_prototypes.h"
|
||||
|
||||
#include "WM_api.h"
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_define.h"
|
||||
#include "RNA_path.h"
|
||||
|
||||
#include "UI_interface.h"
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
#include "UI_interface.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_path.h"
|
||||
|
||||
#include "WM_api.h"
|
||||
#include "WM_types.h"
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_define.h"
|
||||
#include "RNA_path.h"
|
||||
#include "RNA_prototypes.h"
|
||||
#include "RNA_types.h"
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "WM_types.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_path.h"
|
||||
#include "RNA_prototypes.h"
|
||||
|
||||
#include "UI_interface.h"
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
#include "WM_types.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_path.h"
|
||||
|
||||
#include "UI_interface.h"
|
||||
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_define.h"
|
||||
#include "RNA_enum_types.h"
|
||||
#include "RNA_path.h"
|
||||
#include "RNA_prototypes.h"
|
||||
|
||||
#include "ED_keyframing.h"
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include "UI_resources.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_path.h"
|
||||
#include "RNA_prototypes.h"
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "ED_space_api.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_path.h"
|
||||
|
||||
#include "WM_api.h"
|
||||
#include "WM_types.h"
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
#include "WM_types.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_path.h"
|
||||
#include "RNA_prototypes.h"
|
||||
|
||||
#include "ED_anim_api.h"
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_define.h"
|
||||
#include "RNA_path.h"
|
||||
#include "RNA_prototypes.h"
|
||||
|
||||
#include "WM_api.h"
|
||||
|
||||
@@ -55,6 +55,7 @@
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_define.h"
|
||||
#include "RNA_enum_types.h"
|
||||
#include "RNA_path.h"
|
||||
|
||||
#include "GPU_material.h"
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "UI_view2d.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_path.h"
|
||||
|
||||
#include "text_format.h"
|
||||
#include "text_intern.h" /* own include */
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* Copyright 2008 Blender Foundation. All rights reserved. */
|
||||
|
||||
#include "RNA_path.h"
|
||||
|
||||
#include "BCAnimationCurve.h"
|
||||
|
||||
BCAnimationCurve::BCAnimationCurve()
|
||||
|
||||
@@ -475,247 +475,6 @@ bool RNA_property_copy(
|
||||
bool RNA_property_reset(PointerRNA *ptr, PropertyRNA *prop, int index);
|
||||
bool RNA_property_assign_default(PointerRNA *ptr, PropertyRNA *prop);
|
||||
|
||||
/* Path
|
||||
*
|
||||
* Experimental method to refer to structs and properties with a string,
|
||||
* using a syntax like: scenes[0].objects["Cube"].data.verts[7].co
|
||||
*
|
||||
* This provides a way to refer to RNA data while being detached from any
|
||||
* particular pointers, which is useful in a number of applications, like
|
||||
* UI code or Actions, though efficiency is a concern. */
|
||||
|
||||
char *RNA_path_append(
|
||||
const char *path, const PointerRNA *ptr, PropertyRNA *prop, int intkey, const char *strkey);
|
||||
#if 0 /* UNUSED. */
|
||||
char *RNA_path_back(const char *path);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Search for the start of the 'rna array index' part of the given `rna_path`.
|
||||
*
|
||||
* Given the root RNA pointer and resolved RNA property, and the RNA path, return the first
|
||||
* character in `rna_path` that is part of the array index for the given property. Return NULL if
|
||||
* none can be found, e.g. because the property is not an RNA array.
|
||||
*
|
||||
* \param array_prop: if not NULL, the #PropertyRNA assumed to be the last one from the RNA path.
|
||||
* Only used to ensure it is a valid array property.
|
||||
*/
|
||||
const char *RNA_path_array_index_token_find(const char *rna_path, const PropertyRNA *array_prop);
|
||||
|
||||
/* RNA_path_resolve() variants only ensure that a valid pointer (and optionally property) exist. */
|
||||
|
||||
/**
|
||||
* Resolve the given RNA Path to find the pointer and/or property
|
||||
* indicated by fully resolving the path.
|
||||
*
|
||||
* \warning Unlike \a RNA_path_resolve_property(), that one *will* try to follow RNAPointers,
|
||||
* e.g. the path 'parent' applied to a RNAObject \a ptr will return the object.parent in \a r_ptr,
|
||||
* and a NULL \a r_prop...
|
||||
*
|
||||
* \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(const PointerRNA *ptr,
|
||||
const char *path,
|
||||
PointerRNA *r_ptr,
|
||||
PropertyRNA **r_prop);
|
||||
|
||||
/**
|
||||
* 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(const PointerRNA *ptr,
|
||||
const char *path,
|
||||
PointerRNA *r_ptr,
|
||||
PropertyRNA **r_prop,
|
||||
int *r_index);
|
||||
/**
|
||||
* A version of #RNA_path_resolve_full doesn't check the value of #PointerRNA.data.
|
||||
*
|
||||
* \note While it's correct to ignore the value of #PointerRNA.data
|
||||
* most callers need to know if the resulting pointer was found and not null.
|
||||
*/
|
||||
bool RNA_path_resolve_full_maybe_null(const PointerRNA *ptr,
|
||||
const char *path,
|
||||
PointerRNA *r_ptr,
|
||||
PropertyRNA **r_prop,
|
||||
int *r_index);
|
||||
|
||||
/* RNA_path_resolve_property() variants ensure that pointer + property both exist. */
|
||||
|
||||
/**
|
||||
* 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(const PointerRNA *ptr,
|
||||
const char *path,
|
||||
PointerRNA *r_ptr,
|
||||
PropertyRNA **r_prop);
|
||||
|
||||
/**
|
||||
* 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(const PointerRNA *ptr,
|
||||
const char *path,
|
||||
PointerRNA *r_ptr,
|
||||
PropertyRNA **r_prop,
|
||||
int *r_index);
|
||||
|
||||
/* RNA_path_resolve_property_and_item_pointer() variants ensure that pointer + property both exist,
|
||||
* and resolve last Pointer value if possible (Pointer prop or item of a Collection prop). */
|
||||
|
||||
/**
|
||||
* 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 #RNA_path_resolve_property in a single call.
|
||||
* \note Assumes all pointers provided are valid.
|
||||
* \param r_item_ptr: 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(const PointerRNA *ptr,
|
||||
const char *path,
|
||||
PointerRNA *r_ptr,
|
||||
PropertyRNA **r_prop,
|
||||
PointerRNA *r_item_ptr);
|
||||
|
||||
/**
|
||||
* Resolve the given RNA Path to find both the pointer AND property (as well as the array index)
|
||||
* indicated by fully resolving the path,
|
||||
* and get the value of the Pointer property (or item of the collection).
|
||||
*
|
||||
* This is a convenience method to avoid logic errors and ugly syntax,
|
||||
* it combines both \a RNA_path_resolve_full and
|
||||
* \a RNA_path_resolve_property_full in a single call.
|
||||
* \note Assumes all pointers provided are valid.
|
||||
* \param r_item_ptr: 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(const PointerRNA *ptr,
|
||||
const char *path,
|
||||
PointerRNA *r_ptr,
|
||||
PropertyRNA **r_prop,
|
||||
int *r_index,
|
||||
PointerRNA *r_item_ptr);
|
||||
|
||||
typedef struct PropertyElemRNA PropertyElemRNA;
|
||||
struct PropertyElemRNA {
|
||||
PropertyElemRNA *next, *prev;
|
||||
PointerRNA ptr;
|
||||
PropertyRNA *prop;
|
||||
int index;
|
||||
};
|
||||
/**
|
||||
* 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, struct ListBase *r_elements);
|
||||
|
||||
/**
|
||||
* Find the path from the structure referenced by the pointer to the runtime RNA-defined
|
||||
* #IDProperty object.
|
||||
*
|
||||
* \note Does *not* handle pure user-defined IDProperties (a.k.a. custom properties).
|
||||
*
|
||||
* \param ptr: Reference to the object owning the custom property storage.
|
||||
* \param needle: Custom property object to find.
|
||||
* \return Relative path or NULL.
|
||||
*/
|
||||
char *RNA_path_from_struct_to_idproperty(PointerRNA *ptr, struct IDProperty *needle);
|
||||
|
||||
/**
|
||||
* Find the actual ID pointer and path from it to the given ID.
|
||||
*
|
||||
* \param id: ID reference to search the global owner for.
|
||||
* \param[out] r_path: Path from the real ID to the initial ID.
|
||||
* \return The ID pointer, or NULL in case of failure.
|
||||
*/
|
||||
struct ID *RNA_find_real_ID_and_path(struct Main *bmain, struct ID *id, const char **r_path);
|
||||
|
||||
char *RNA_path_from_ID_to_struct(const PointerRNA *ptr);
|
||||
|
||||
char *RNA_path_from_real_ID_to_struct(struct Main *bmain,
|
||||
const PointerRNA *ptr,
|
||||
struct ID **r_real);
|
||||
|
||||
char *RNA_path_from_ID_to_property(const PointerRNA *ptr, PropertyRNA *prop);
|
||||
/**
|
||||
* \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(const PointerRNA *ptr,
|
||||
PropertyRNA *prop,
|
||||
int index_dim,
|
||||
int index);
|
||||
|
||||
char *RNA_path_from_real_ID_to_property_index(struct Main *bmain,
|
||||
const PointerRNA *ptr,
|
||||
PropertyRNA *prop,
|
||||
int index_dim,
|
||||
int index,
|
||||
struct ID **r_real_id);
|
||||
|
||||
/**
|
||||
* \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(const PointerRNA *ptr,
|
||||
PropertyRNA *prop,
|
||||
const struct StructRNA *type);
|
||||
|
||||
/**
|
||||
* Get the ID as a python representation, eg:
|
||||
* bpy.data.foo["bar"]
|
||||
*/
|
||||
char *RNA_path_full_ID_py(struct Main *bmain, struct ID *id);
|
||||
/**
|
||||
* Get the ID.struct as a python representation, eg:
|
||||
* bpy.data.foo["bar"].some_struct
|
||||
*/
|
||||
char *RNA_path_full_struct_py(struct Main *bmain, const PointerRNA *ptr);
|
||||
/**
|
||||
* 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(
|
||||
struct Main *bmain, const PointerRNA *ptr, PropertyRNA *prop, int index, bool use_fallback);
|
||||
char *RNA_path_full_property_py(struct Main *bmain,
|
||||
const PointerRNA *ptr,
|
||||
PropertyRNA *prop,
|
||||
int index);
|
||||
/**
|
||||
* 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);
|
||||
/**
|
||||
* Get the struct.property as a python representation, eg:
|
||||
* some_prop[10]
|
||||
*/
|
||||
char *RNA_path_property_py(const PointerRNA *ptr, PropertyRNA *prop, int index);
|
||||
|
||||
/* Quick name based property access
|
||||
*
|
||||
* These are just an easier way to access property values without having to
|
||||
|
||||
259
source/blender/makesrna/RNA_path.h
Normal file
259
source/blender/makesrna/RNA_path.h
Normal file
@@ -0,0 +1,259 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup RNA
|
||||
*
|
||||
* RNA paths are a way to refer to pointers and properties with a string,
|
||||
* using a syntax like: scenes[0].objects["Cube"].data.verts[7].co
|
||||
*
|
||||
* This provides a way to refer to RNA data while being detached from any
|
||||
* particular pointers, which is useful in a number of applications, like
|
||||
* UI code or Actions, though efficiency is a concern.
|
||||
*/
|
||||
|
||||
#include "RNA_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct ListBase;
|
||||
struct IDProperty;
|
||||
|
||||
char *RNA_path_append(
|
||||
const char *path, const PointerRNA *ptr, PropertyRNA *prop, int intkey, const char *strkey);
|
||||
#if 0 /* UNUSED. */
|
||||
char *RNA_path_back(const char *path);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Search for the start of the 'rna array index' part of the given `rna_path`.
|
||||
*
|
||||
* Given the root RNA pointer and resolved RNA property, and the RNA path, return the first
|
||||
* character in `rna_path` that is part of the array index for the given property. Return NULL if
|
||||
* none can be found, e.g. because the property is not an RNA array.
|
||||
*
|
||||
* \param array_prop: if not NULL, the #PropertyRNA assumed to be the last one from the RNA path.
|
||||
* Only used to ensure it is a valid array property.
|
||||
*/
|
||||
const char *RNA_path_array_index_token_find(const char *rna_path, const PropertyRNA *array_prop);
|
||||
|
||||
/* RNA_path_resolve() variants only ensure that a valid pointer (and optionally property) exist. */
|
||||
|
||||
/**
|
||||
* Resolve the given RNA Path to find the pointer and/or property
|
||||
* indicated by fully resolving the path.
|
||||
*
|
||||
* \warning Unlike \a RNA_path_resolve_property(), that one *will* try to follow RNAPointers,
|
||||
* e.g. the path 'parent' applied to a RNAObject \a ptr will return the object.parent in \a r_ptr,
|
||||
* and a NULL \a r_prop...
|
||||
*
|
||||
* \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(const PointerRNA *ptr,
|
||||
const char *path,
|
||||
PointerRNA *r_ptr,
|
||||
PropertyRNA **r_prop);
|
||||
|
||||
/**
|
||||
* 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(const PointerRNA *ptr,
|
||||
const char *path,
|
||||
PointerRNA *r_ptr,
|
||||
PropertyRNA **r_prop,
|
||||
int *r_index);
|
||||
/**
|
||||
* A version of #RNA_path_resolve_full doesn't check the value of #PointerRNA.data.
|
||||
*
|
||||
* \note While it's correct to ignore the value of #PointerRNA.data
|
||||
* most callers need to know if the resulting pointer was found and not null.
|
||||
*/
|
||||
bool RNA_path_resolve_full_maybe_null(const PointerRNA *ptr,
|
||||
const char *path,
|
||||
PointerRNA *r_ptr,
|
||||
PropertyRNA **r_prop,
|
||||
int *r_index);
|
||||
|
||||
/* RNA_path_resolve_property() variants ensure that pointer + property both exist. */
|
||||
|
||||
/**
|
||||
* 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(const PointerRNA *ptr,
|
||||
const char *path,
|
||||
PointerRNA *r_ptr,
|
||||
PropertyRNA **r_prop);
|
||||
|
||||
/**
|
||||
* 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(const PointerRNA *ptr,
|
||||
const char *path,
|
||||
PointerRNA *r_ptr,
|
||||
PropertyRNA **r_prop,
|
||||
int *r_index);
|
||||
|
||||
/* RNA_path_resolve_property_and_item_pointer() variants ensure that pointer + property both exist,
|
||||
* and resolve last Pointer value if possible (Pointer prop or item of a Collection prop). */
|
||||
|
||||
/**
|
||||
* 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 #RNA_path_resolve_property in a single call.
|
||||
* \note Assumes all pointers provided are valid.
|
||||
* \param r_item_ptr: 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(const PointerRNA *ptr,
|
||||
const char *path,
|
||||
PointerRNA *r_ptr,
|
||||
PropertyRNA **r_prop,
|
||||
PointerRNA *r_item_ptr);
|
||||
|
||||
/**
|
||||
* Resolve the given RNA Path to find both the pointer AND property (as well as the array index)
|
||||
* indicated by fully resolving the path,
|
||||
* and get the value of the Pointer property (or item of the collection).
|
||||
*
|
||||
* This is a convenience method to avoid logic errors and ugly syntax,
|
||||
* it combines both \a RNA_path_resolve_full and
|
||||
* \a RNA_path_resolve_property_full in a single call.
|
||||
* \note Assumes all pointers provided are valid.
|
||||
* \param r_item_ptr: 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(const PointerRNA *ptr,
|
||||
const char *path,
|
||||
PointerRNA *r_ptr,
|
||||
PropertyRNA **r_prop,
|
||||
int *r_index,
|
||||
PointerRNA *r_item_ptr);
|
||||
|
||||
typedef struct PropertyElemRNA PropertyElemRNA;
|
||||
struct PropertyElemRNA {
|
||||
PropertyElemRNA *next, *prev;
|
||||
PointerRNA ptr;
|
||||
PropertyRNA *prop;
|
||||
int index;
|
||||
};
|
||||
/**
|
||||
* 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, struct ListBase *r_elements);
|
||||
|
||||
/**
|
||||
* Find the path from the structure referenced by the pointer to the runtime RNA-defined
|
||||
* #IDProperty object.
|
||||
*
|
||||
* \note Does *not* handle pure user-defined IDProperties (a.k.a. custom properties).
|
||||
*
|
||||
* \param ptr: Reference to the object owning the custom property storage.
|
||||
* \param needle: Custom property object to find.
|
||||
* \return Relative path or NULL.
|
||||
*/
|
||||
char *RNA_path_from_struct_to_idproperty(PointerRNA *ptr, struct IDProperty *needle);
|
||||
|
||||
/**
|
||||
* Find the actual ID pointer and path from it to the given ID.
|
||||
*
|
||||
* \param id: ID reference to search the global owner for.
|
||||
* \param[out] r_path: Path from the real ID to the initial ID.
|
||||
* \return The ID pointer, or NULL in case of failure.
|
||||
*/
|
||||
struct ID *RNA_find_real_ID_and_path(struct Main *bmain, struct ID *id, const char **r_path);
|
||||
|
||||
char *RNA_path_from_ID_to_struct(const PointerRNA *ptr);
|
||||
|
||||
char *RNA_path_from_real_ID_to_struct(struct Main *bmain,
|
||||
const PointerRNA *ptr,
|
||||
struct ID **r_real);
|
||||
|
||||
char *RNA_path_from_ID_to_property(const PointerRNA *ptr, PropertyRNA *prop);
|
||||
/**
|
||||
* \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(const PointerRNA *ptr,
|
||||
PropertyRNA *prop,
|
||||
int index_dim,
|
||||
int index);
|
||||
|
||||
char *RNA_path_from_real_ID_to_property_index(struct Main *bmain,
|
||||
const PointerRNA *ptr,
|
||||
PropertyRNA *prop,
|
||||
int index_dim,
|
||||
int index,
|
||||
struct ID **r_real_id);
|
||||
|
||||
/**
|
||||
* \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(const PointerRNA *ptr,
|
||||
PropertyRNA *prop,
|
||||
const struct StructRNA *type);
|
||||
|
||||
/**
|
||||
* Get the ID as a python representation, eg:
|
||||
* bpy.data.foo["bar"]
|
||||
*/
|
||||
char *RNA_path_full_ID_py(struct Main *bmain, struct ID *id);
|
||||
/**
|
||||
* Get the ID.struct as a python representation, eg:
|
||||
* bpy.data.foo["bar"].some_struct
|
||||
*/
|
||||
char *RNA_path_full_struct_py(struct Main *bmain, const PointerRNA *ptr);
|
||||
/**
|
||||
* 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(
|
||||
struct Main *bmain, const PointerRNA *ptr, PropertyRNA *prop, int index, bool use_fallback);
|
||||
char *RNA_path_full_property_py(struct Main *bmain,
|
||||
const PointerRNA *ptr,
|
||||
PropertyRNA *prop,
|
||||
int index);
|
||||
/**
|
||||
* 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);
|
||||
/**
|
||||
* Get the struct.property as a python representation, eg:
|
||||
* some_prop[10]
|
||||
*/
|
||||
char *RNA_path_property_py(const PointerRNA *ptr, PropertyRNA *prop, int index);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -159,6 +159,7 @@ set(SRC_RNA_INC
|
||||
../RNA_documentation.h
|
||||
../RNA_enum_items.h
|
||||
../RNA_enum_types.h
|
||||
../RNA_path.h
|
||||
../RNA_types.h
|
||||
)
|
||||
|
||||
@@ -416,6 +417,7 @@ add_custom_command(
|
||||
set(SRC
|
||||
rna_access.c
|
||||
rna_access_compare_override.c
|
||||
rna_path.cc
|
||||
${GENSRC}
|
||||
|
||||
${SRC_RNA_INC}
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_define.h"
|
||||
#include "RNA_enum_types.h"
|
||||
#include "RNA_path.h"
|
||||
|
||||
#include "WM_api.h"
|
||||
#include "WM_message.h"
|
||||
@@ -738,7 +739,7 @@ PropertyRNA *RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
|
||||
}
|
||||
|
||||
/* Find the property which uses the given nested struct */
|
||||
static PropertyRNA *RNA_struct_find_nested(PointerRNA *ptr, StructRNA *srna)
|
||||
PropertyRNA *rna_struct_find_nested(PointerRNA *ptr, StructRNA *srna)
|
||||
{
|
||||
PropertyRNA *prop = NULL;
|
||||
|
||||
@@ -1422,7 +1423,7 @@ StructRNA *RNA_property_pointer_type(PointerRNA *ptr, PropertyRNA *prop)
|
||||
return cprop->item_type;
|
||||
}
|
||||
}
|
||||
/* ignore other types, RNA_struct_find_nested calls with unchecked props */
|
||||
/* ignore other types, rna_struct_find_nested calls with unchecked props */
|
||||
|
||||
return &RNA_UnknownType;
|
||||
}
|
||||
@@ -4821,1337 +4822,6 @@ PointerRNA rna_array_lookup_int(
|
||||
return rna_pointer_inherit_refine(ptr, type, ((char *)data) + index * itemsize);
|
||||
}
|
||||
|
||||
/* RNA Path - Experiment */
|
||||
|
||||
/**
|
||||
* Extract the first token from `path`.
|
||||
*
|
||||
* \param path: Extract the token from path, step the pointer to the beginning of the next token
|
||||
* \return The nil terminated token.
|
||||
*/
|
||||
static char *rna_path_token(const char **path, char *fixedbuf, int fixedlen)
|
||||
{
|
||||
int len = 0;
|
||||
|
||||
/* Get data until `.` or `[`. */
|
||||
const char *p = *path;
|
||||
while (*p && !ELEM(*p, '.', '[')) {
|
||||
len++;
|
||||
p++;
|
||||
}
|
||||
|
||||
/* Empty, return. */
|
||||
if (UNLIKELY(len == 0)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Try to use fixed buffer if possible. */
|
||||
char *buf = (len + 1 < fixedlen) ? fixedbuf : MEM_mallocN(sizeof(char) * (len + 1), __func__);
|
||||
memcpy(buf, *path, sizeof(char) * len);
|
||||
buf[len] = '\0';
|
||||
|
||||
if (*p == '.') {
|
||||
p++;
|
||||
}
|
||||
*path = p;
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the first token in brackets from `path` (with quoted text support).
|
||||
*
|
||||
* - `[0]` -> `0`
|
||||
* - `["Some\"Quote"]` -> `Some"Quote`
|
||||
*
|
||||
* \param path: Extract the token from path, step the pointer to the beginning of the next token
|
||||
* (past quoted text and brackets).
|
||||
* \return The nil terminated token.
|
||||
*/
|
||||
static char *rna_path_token_in_brackets(const char **path,
|
||||
char *fixedbuf,
|
||||
int fixedlen,
|
||||
bool *r_quoted)
|
||||
{
|
||||
int len = 0;
|
||||
bool quoted = false;
|
||||
|
||||
BLI_assert(r_quoted != NULL);
|
||||
|
||||
/* Get data between `[]`, check escaping quotes and back-slashes with #BLI_str_unescape. */
|
||||
if (UNLIKELY(**path != '[')) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
(*path)++;
|
||||
const char *p = *path;
|
||||
|
||||
/* 2 kinds of look-ups now, quoted or unquoted. */
|
||||
if (*p == '"') {
|
||||
/* Find the matching quote. */
|
||||
(*path)++;
|
||||
p = *path;
|
||||
const char *p_end = BLI_str_escape_find_quote(p);
|
||||
if (p_end == NULL) {
|
||||
/* No Matching quote. */
|
||||
return NULL;
|
||||
}
|
||||
/* Exclude the last quote from the length. */
|
||||
len += (p_end - p);
|
||||
|
||||
/* Skip the last quoted char to get the `]`. */
|
||||
p_end += 1;
|
||||
p = p_end;
|
||||
quoted = true;
|
||||
}
|
||||
else {
|
||||
/* Find the matching bracket. */
|
||||
while (*p && (*p != ']')) {
|
||||
len++;
|
||||
p++;
|
||||
}
|
||||
}
|
||||
|
||||
if (UNLIKELY(*p != ']')) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Empty, return. */
|
||||
if (UNLIKELY(len == 0)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Try to use fixed buffer if possible. */
|
||||
char *buf = (len + 1 < fixedlen) ? fixedbuf : MEM_mallocN(sizeof(char) * (len + 1), __func__);
|
||||
|
||||
/* Copy string, taking into account escaped ']' */
|
||||
if (quoted) {
|
||||
BLI_str_unescape(buf, *path, len);
|
||||
/* +1 to step over the last quote. */
|
||||
BLI_assert((*path)[len] == '"');
|
||||
p = (*path) + len + 1;
|
||||
}
|
||||
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;
|
||||
|
||||
*r_quoted = quoted;
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* \return true when the key in the path is correctly parsed and found in the collection
|
||||
* or when the path is empty.
|
||||
*/
|
||||
static bool rna_path_parse_collection_key(const char **path,
|
||||
PointerRNA *ptr,
|
||||
PropertyRNA *prop,
|
||||
PointerRNA *r_nextptr)
|
||||
{
|
||||
char fixedbuf[256];
|
||||
int intkey;
|
||||
|
||||
*r_nextptr = *ptr;
|
||||
|
||||
/* end of path, ok */
|
||||
if (!(**path)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool found = false;
|
||||
if (**path == '[') {
|
||||
bool quoted;
|
||||
char *token;
|
||||
|
||||
/* resolve the lookup with [] brackets */
|
||||
token = rna_path_token_in_brackets(path, fixedbuf, sizeof(fixedbuf), "ed);
|
||||
|
||||
if (!token) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* check for "" to see if it is a string */
|
||||
if (quoted) {
|
||||
if (RNA_property_collection_lookup_string(ptr, prop, token, r_nextptr)) {
|
||||
found = true;
|
||||
}
|
||||
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)) {
|
||||
found = true;
|
||||
}
|
||||
else {
|
||||
r_nextptr->data = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (token != fixedbuf) {
|
||||
MEM_freeN(token);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (RNA_property_collection_type_get(ptr, prop, r_nextptr)) {
|
||||
found = true;
|
||||
}
|
||||
else {
|
||||
/* ensure we quit on invalid values */
|
||||
r_nextptr->data = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
static bool rna_path_parse_array_index(const char **path,
|
||||
PointerRNA *ptr,
|
||||
PropertyRNA *prop,
|
||||
int *r_index)
|
||||
{
|
||||
char fixedbuf[256];
|
||||
int index_arr[RNA_MAX_ARRAY_DIMENSION] = {0};
|
||||
int len[RNA_MAX_ARRAY_DIMENSION];
|
||||
const int dim = RNA_property_array_dimension(ptr, prop, len);
|
||||
int i;
|
||||
|
||||
*r_index = -1;
|
||||
|
||||
/* end of path, ok */
|
||||
if (!(**path)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (i = 0; i < dim; i++) {
|
||||
int temp_index = -1;
|
||||
char *token;
|
||||
|
||||
/* multi index resolve */
|
||||
if (**path == '[') {
|
||||
bool quoted;
|
||||
token = rna_path_token_in_brackets(path, fixedbuf, sizeof(fixedbuf), "ed);
|
||||
|
||||
if (token == NULL) {
|
||||
/* invalid syntax blah[] */
|
||||
return false;
|
||||
}
|
||||
/* check for "" to see if it is a string */
|
||||
if (quoted) {
|
||||
temp_index = RNA_property_array_item_index(prop, *token);
|
||||
}
|
||||
else {
|
||||
/* otherwise do int lookup */
|
||||
temp_index = atoi(token);
|
||||
|
||||
if (temp_index == 0 && (token[0] != '0' || token[1] != '\0')) {
|
||||
if (token != fixedbuf) {
|
||||
MEM_freeN(token);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (dim == 1) {
|
||||
/* location.x || scale.X, single dimension arrays only */
|
||||
token = rna_path_token(path, fixedbuf, sizeof(fixedbuf));
|
||||
if (token == NULL) {
|
||||
/* invalid syntax blah. */
|
||||
return false;
|
||||
}
|
||||
temp_index = RNA_property_array_item_index(prop, *token);
|
||||
}
|
||||
else {
|
||||
/* just to avoid uninitialized pointer use */
|
||||
token = fixedbuf;
|
||||
}
|
||||
|
||||
if (token != fixedbuf) {
|
||||
MEM_freeN(token);
|
||||
}
|
||||
|
||||
/* out of range */
|
||||
if (temp_index < 0 || temp_index >= len[i]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
index_arr[i] = temp_index;
|
||||
/* end multi index resolve */
|
||||
}
|
||||
|
||||
/* arrays always contain numbers so further values are not valid */
|
||||
if (**path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* flatten index over all dimensions */
|
||||
{
|
||||
int totdim = 1;
|
||||
int flat_index = 0;
|
||||
|
||||
for (i = dim - 1; i >= 0; i--) {
|
||||
flat_index += index_arr[i] * totdim;
|
||||
totdim *= len[i];
|
||||
}
|
||||
|
||||
*r_index = flat_index;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic rna path parser.
|
||||
*
|
||||
* \note All parameters besides \a ptr and \a path are optional.
|
||||
*
|
||||
* \param ptr: The root of given RNA path.
|
||||
* \param path: The RNA path.
|
||||
* \param r_ptr: The final RNA data holding the last property in \a path.
|
||||
* \param r_prop: The final property of \a r_ptr, from \a path.
|
||||
* \param r_index: The final index in the \a r_prop, if defined by \a path.
|
||||
* \param r_item_ptr: Only valid for Pointer and Collection, return the actual value of the
|
||||
* pointer, or of the collection item.
|
||||
* Mutually exclusive with \a eval_pointer option.
|
||||
* \param r_elements: A list of \a PropertyElemRNA items(pairs of \a PointerRNA, \a PropertyRNA
|
||||
* that represent the whole given \a path).
|
||||
* \param eval_pointer: If \a true, and \a path leads to a Pointer property, or an item in a
|
||||
* Collection property, \a r_ptr will be set to the value of that property,
|
||||
* and \a r_prop will be NULL.
|
||||
* Mutually exclusive with \a r_item_ptr.
|
||||
*
|
||||
* \return \a true on success, \a false if the path is somehow invalid.
|
||||
*/
|
||||
static bool rna_path_parse(const 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);
|
||||
}
|
||||
|
||||
const bool use_id_prop = (*path == '[');
|
||||
/* custom property lookup ?
|
||||
* C.object["someprop"]
|
||||
*/
|
||||
|
||||
if (!curptr.data) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* look up property name in current struct */
|
||||
bool quoted = false;
|
||||
char *token = use_id_prop ?
|
||||
rna_path_token_in_brackets(&path, fixedbuf, sizeof(fixedbuf), "ed) :
|
||||
rna_path_token(&path, fixedbuf, sizeof(fixedbuf));
|
||||
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 && quoted) {
|
||||
prop = (PropertyRNA *)IDP_GetPropertyFromGroup(group, token);
|
||||
}
|
||||
}
|
||||
else {
|
||||
prop = RNA_struct_find_property(&curptr, token);
|
||||
}
|
||||
|
||||
if (token != fixedbuf) {
|
||||
MEM_freeN(token);
|
||||
}
|
||||
|
||||
if (!prop) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (r_elements) {
|
||||
prop_elem = MEM_mallocN(sizeof(PropertyElemRNA), __func__);
|
||||
prop_elem->ptr = curptr;
|
||||
prop_elem->prop = prop;
|
||||
prop_elem->index = -1; /* index will be added later, if needed. */
|
||||
BLI_addtail(r_elements, prop_elem);
|
||||
}
|
||||
|
||||
type = RNA_property_type(prop);
|
||||
|
||||
/* now look up the value of this property if it is a pointer or
|
||||
* collection, otherwise return the property rna so that the
|
||||
* caller can read the value of the property itself */
|
||||
switch (type) {
|
||||
case PROP_POINTER: {
|
||||
/* resolve pointer if further path elements follow
|
||||
* or explicitly requested
|
||||
*/
|
||||
if (do_item_ptr || eval_pointer || *path != '\0') {
|
||||
nextptr = RNA_property_pointer_get(&curptr, prop);
|
||||
}
|
||||
|
||||
if (eval_pointer || *path != '\0') {
|
||||
curptr = nextptr;
|
||||
prop = NULL; /* now we have a PointerRNA, the prop is our parent so forget it */
|
||||
index = -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PROP_COLLECTION: {
|
||||
/* Resolve pointer if further path elements follow.
|
||||
* Note that if path is empty, rna_path_parse_collection_key will do nothing anyway,
|
||||
* so do_item_ptr is of no use in that case.
|
||||
*/
|
||||
if (*path) {
|
||||
if (!rna_path_parse_collection_key(&path, &curptr, prop, &nextptr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (eval_pointer || *path != '\0') {
|
||||
curptr = nextptr;
|
||||
prop = NULL; /* now we have a PointerRNA, the prop is our parent so forget it */
|
||||
index = -1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
if (r_index || prop_elem) {
|
||||
if (!rna_path_parse_array_index(&path, &curptr, prop, &index)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (prop_elem) {
|
||||
prop_elem->index = index;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (r_ptr) {
|
||||
*r_ptr = curptr;
|
||||
}
|
||||
if (r_prop) {
|
||||
*r_prop = prop;
|
||||
}
|
||||
if (r_index) {
|
||||
*r_index = index;
|
||||
}
|
||||
if (r_item_ptr && do_item_ptr) {
|
||||
*r_item_ptr = nextptr;
|
||||
}
|
||||
|
||||
if (prop_elem && (prop_elem->ptr.data != curptr.data || prop_elem->prop != prop ||
|
||||
prop_elem->index != index)) {
|
||||
prop_elem = MEM_mallocN(sizeof(PropertyElemRNA), __func__);
|
||||
prop_elem->ptr = curptr;
|
||||
prop_elem->prop = prop;
|
||||
prop_elem->index = index;
|
||||
BLI_addtail(r_elements, prop_elem);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RNA_path_resolve(const 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;
|
||||
}
|
||||
|
||||
bool RNA_path_resolve_full(
|
||||
const 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;
|
||||
}
|
||||
|
||||
bool RNA_path_resolve_full_maybe_null(
|
||||
const PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop, int *r_index)
|
||||
{
|
||||
return rna_path_parse(ptr, path, r_ptr, r_prop, r_index, NULL, NULL, true);
|
||||
}
|
||||
|
||||
bool RNA_path_resolve_property(const 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;
|
||||
}
|
||||
|
||||
bool RNA_path_resolve_property_full(
|
||||
const 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;
|
||||
}
|
||||
|
||||
bool RNA_path_resolve_property_and_item_pointer(const 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;
|
||||
}
|
||||
|
||||
bool RNA_path_resolve_property_and_item_pointer_full(const 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;
|
||||
}
|
||||
bool RNA_path_resolve_elements(PointerRNA *ptr, const char *path, ListBase *r_elements)
|
||||
{
|
||||
return rna_path_parse(ptr, path, NULL, NULL, NULL, NULL, r_elements, false);
|
||||
}
|
||||
|
||||
char *RNA_path_append(const char *path,
|
||||
const PointerRNA *UNUSED(ptr),
|
||||
PropertyRNA *prop,
|
||||
int intkey,
|
||||
const char *strkey)
|
||||
{
|
||||
DynStr *dynstr;
|
||||
char *result;
|
||||
|
||||
dynstr = BLI_dynstr_new();
|
||||
|
||||
/* add .identifier */
|
||||
if (path) {
|
||||
BLI_dynstr_append(dynstr, path);
|
||||
if (*path) {
|
||||
BLI_dynstr_append(dynstr, ".");
|
||||
}
|
||||
}
|
||||
|
||||
BLI_dynstr_append(dynstr, RNA_property_identifier(prop));
|
||||
|
||||
if (RNA_property_type(prop) == PROP_COLLECTION) {
|
||||
/* add ["strkey"] or [intkey] */
|
||||
BLI_dynstr_append(dynstr, "[");
|
||||
|
||||
if (strkey) {
|
||||
const int strkey_esc_max_size = (strlen(strkey) * 2) + 1;
|
||||
char *strkey_esc = BLI_array_alloca(strkey_esc, strkey_esc_max_size);
|
||||
BLI_str_escape(strkey_esc, strkey, strkey_esc_max_size);
|
||||
BLI_dynstr_append(dynstr, "\"");
|
||||
BLI_dynstr_append(dynstr, strkey_esc);
|
||||
BLI_dynstr_append(dynstr, "\"");
|
||||
}
|
||||
else {
|
||||
char appendstr[128];
|
||||
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;
|
||||
}
|
||||
|
||||
/* Having both path append & back seems like it could be useful,
|
||||
* this function isn't used at the moment. */
|
||||
static UNUSED_FUNCTION_WITH_RETURN_TYPE(char *, RNA_path_back)(const char *path)
|
||||
{
|
||||
char fixedbuf[256];
|
||||
const char *previous, *current;
|
||||
char *result;
|
||||
int i;
|
||||
|
||||
if (!path) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
previous = NULL;
|
||||
current = path;
|
||||
|
||||
/* parse token by token until the end, then we back up to the previous
|
||||
* position and strip of the next token to get the path one step back */
|
||||
while (*current) {
|
||||
char *token;
|
||||
|
||||
token = rna_path_token(¤t, fixedbuf, sizeof(fixedbuf));
|
||||
|
||||
if (!token) {
|
||||
return NULL;
|
||||
}
|
||||
if (token != fixedbuf) {
|
||||
MEM_freeN(token);
|
||||
}
|
||||
|
||||
/* in case of collection we also need to strip off [] */
|
||||
bool quoted;
|
||||
token = rna_path_token_in_brackets(¤t, fixedbuf, sizeof(fixedbuf), "ed);
|
||||
if (token && token != fixedbuf) {
|
||||
MEM_freeN(token);
|
||||
}
|
||||
|
||||
if (!*current) {
|
||||
break;
|
||||
}
|
||||
|
||||
previous = current;
|
||||
}
|
||||
|
||||
if (!previous) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* copy and strip off last token */
|
||||
i = previous - path;
|
||||
result = BLI_strdup(path);
|
||||
|
||||
if (i > 0 && result[i - 1] == '.') {
|
||||
i--;
|
||||
}
|
||||
result[i] = 0;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const char *RNA_path_array_index_token_find(const char *rna_path, const PropertyRNA *array_prop)
|
||||
{
|
||||
if (array_prop != NULL) {
|
||||
if (!ELEM(array_prop->type, PROP_BOOLEAN, PROP_INT, PROP_FLOAT)) {
|
||||
BLI_assert(array_prop->arraydimension == 0);
|
||||
return NULL;
|
||||
}
|
||||
if (array_prop->arraydimension == 0) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Valid 'array part' of a rna path can only have '[', ']' and digit characters.
|
||||
* It may have more than one of those (e.g. `[12][1]`) in case of multi-dimensional arrays. */
|
||||
off_t rna_path_len = (off_t)strlen(rna_path);
|
||||
if (rna_path[rna_path_len] != ']') {
|
||||
return NULL;
|
||||
}
|
||||
const char *last_valid_index_token_start = NULL;
|
||||
for (rna_path_len--; rna_path_len >= 0; rna_path_len--) {
|
||||
switch (rna_path[rna_path_len]) {
|
||||
case '[':
|
||||
if (rna_path_len <= 0 || rna_path[rna_path_len - 1] != ']') {
|
||||
return &rna_path[rna_path_len];
|
||||
}
|
||||
last_valid_index_token_start = &rna_path[rna_path_len];
|
||||
rna_path_len--;
|
||||
break;
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
break;
|
||||
default:
|
||||
return last_valid_index_token_start;
|
||||
}
|
||||
}
|
||||
return last_valid_index_token_start;
|
||||
}
|
||||
|
||||
/* generic path search func
|
||||
* if its needed this could also reference the IDProperty direct */
|
||||
typedef struct IDP_Chain {
|
||||
struct IDP_Chain *up; /* parent member, reverse and set to child for path conversion. */
|
||||
|
||||
const char *name;
|
||||
int index;
|
||||
|
||||
} IDP_Chain;
|
||||
|
||||
static char *rna_idp_path_create(IDP_Chain *child_link)
|
||||
{
|
||||
DynStr *dynstr = BLI_dynstr_new();
|
||||
char *path;
|
||||
bool is_first = true;
|
||||
|
||||
int tot = 0;
|
||||
IDP_Chain *link = child_link;
|
||||
|
||||
/* reverse the list */
|
||||
IDP_Chain *link_prev;
|
||||
link_prev = NULL;
|
||||
while (link) {
|
||||
IDP_Chain *link_next = link->up;
|
||||
link->up = link_prev;
|
||||
link_prev = link;
|
||||
link = link_next;
|
||||
tot++;
|
||||
}
|
||||
|
||||
for (link = link_prev; link; link = link->up) {
|
||||
/* pass */
|
||||
if (link->index >= 0) {
|
||||
BLI_dynstr_appendf(dynstr, is_first ? "%s[%d]" : ".%s[%d]", link->name, link->index);
|
||||
}
|
||||
else {
|
||||
BLI_dynstr_appendf(dynstr, is_first ? "%s" : ".%s", link->name);
|
||||
}
|
||||
|
||||
is_first = false;
|
||||
}
|
||||
|
||||
path = BLI_dynstr_get_cstring(dynstr);
|
||||
BLI_dynstr_free(dynstr);
|
||||
|
||||
if (*path == '\0') {
|
||||
MEM_freeN(path);
|
||||
path = NULL;
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
static char *rna_idp_path(PointerRNA *ptr,
|
||||
IDProperty *haystack,
|
||||
IDProperty *needle,
|
||||
IDP_Chain *parent_link)
|
||||
{
|
||||
char *path = NULL;
|
||||
IDP_Chain link;
|
||||
|
||||
IDProperty *iter;
|
||||
int i;
|
||||
|
||||
BLI_assert(haystack->type == IDP_GROUP);
|
||||
|
||||
link.up = parent_link;
|
||||
/* Always set both name and index, else a stale value might get used. */
|
||||
link.name = NULL;
|
||||
link.index = -1;
|
||||
|
||||
for (i = 0, iter = haystack->data.group.first; iter; iter = iter->next, i++) {
|
||||
if (needle == iter) { /* found! */
|
||||
link.name = iter->name;
|
||||
link.index = -1;
|
||||
path = rna_idp_path_create(&link);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Early out in case the IDProperty type cannot contain RNA properties. */
|
||||
if (!ELEM(iter->type, IDP_GROUP, IDP_IDPARRAY)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Ensure this is RNA. */
|
||||
/* NOTE: `iter` might be a fully user-defined IDProperty (a.k.a. custom data), which name
|
||||
* collides with an actual fully static RNA property of the same struct (which would then not
|
||||
* be flagged with `PROP_IDPROPERTY`).
|
||||
*
|
||||
* That case must be ignored here, we only want to deal with runtime RNA properties stored in
|
||||
* IDProps.
|
||||
*
|
||||
* See T84091. */
|
||||
PropertyRNA *prop = RNA_struct_find_property(ptr, iter->name);
|
||||
if (prop == NULL || (prop->flag & PROP_IDPROPERTY) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (iter->type == IDP_GROUP) {
|
||||
if (prop->type == PROP_POINTER) {
|
||||
PointerRNA child_ptr = RNA_property_pointer_get(ptr, prop);
|
||||
if (RNA_pointer_is_null(&child_ptr)) {
|
||||
/* Pointer ID prop might be a 'leaf' in the IDProp group hierarchy, in which case a NULL
|
||||
* value is perfectly valid. Just means it won't match the searched needle. */
|
||||
continue;
|
||||
}
|
||||
|
||||
link.name = iter->name;
|
||||
link.index = -1;
|
||||
if ((path = rna_idp_path(&child_ptr, iter, needle, &link))) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (iter->type == IDP_IDPARRAY) {
|
||||
if (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;
|
||||
}
|
||||
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)) {
|
||||
if (RNA_pointer_is_null(&child_ptr)) {
|
||||
/* Array item ID prop might be a 'leaf' in the IDProp group hierarchy, in which case
|
||||
* a NULL value is perfectly valid. Just means it won't match the searched needle. */
|
||||
continue;
|
||||
}
|
||||
link.index = j;
|
||||
if ((path = rna_idp_path(&child_ptr, array, needle, &link))) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (path) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
char *RNA_path_from_struct_to_idproperty(PointerRNA *ptr, IDProperty *needle)
|
||||
{
|
||||
IDProperty *haystack = RNA_struct_idprops(ptr, false);
|
||||
|
||||
if (haystack) { /* can fail when called on bones */
|
||||
return rna_idp_path(ptr, haystack, needle, NULL);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static char *rna_path_from_ID_to_idpgroup(const PointerRNA *ptr)
|
||||
{
|
||||
PointerRNA id_ptr;
|
||||
|
||||
BLI_assert(ptr->owner_id != NULL);
|
||||
|
||||
/* TODO: Support Bones/PoseBones. no pointers stored to the bones from here, only the ID.
|
||||
* See example in T25746.
|
||||
* 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->owner_id, &id_ptr);
|
||||
|
||||
return RNA_path_from_struct_to_idproperty(&id_ptr, ptr->data);
|
||||
}
|
||||
|
||||
ID *RNA_find_real_ID_and_path(Main *bmain, ID *id, const char **r_path)
|
||||
{
|
||||
if (r_path) {
|
||||
*r_path = "";
|
||||
}
|
||||
|
||||
if ((id == NULL) || (id->flag & LIB_EMBEDDED_DATA) == 0) {
|
||||
return id;
|
||||
}
|
||||
|
||||
const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id);
|
||||
if (r_path) {
|
||||
switch (GS(id->name)) {
|
||||
case ID_NT:
|
||||
*r_path = "node_tree";
|
||||
break;
|
||||
case ID_GR:
|
||||
*r_path = "collection";
|
||||
break;
|
||||
default:
|
||||
BLI_assert_msg(0, "Missing handling of embedded id type.");
|
||||
}
|
||||
}
|
||||
|
||||
if (id_type->owner_get == NULL) {
|
||||
BLI_assert_msg(0, "Missing handling of embedded id type.");
|
||||
return id;
|
||||
}
|
||||
return id_type->owner_get(bmain, id);
|
||||
}
|
||||
|
||||
static char *rna_prepend_real_ID_path(Main *bmain, ID *id, char *path, ID **r_real_id)
|
||||
{
|
||||
if (r_real_id != NULL) {
|
||||
*r_real_id = NULL;
|
||||
}
|
||||
|
||||
const char *prefix;
|
||||
ID *real_id = RNA_find_real_ID_and_path(bmain, id, &prefix);
|
||||
|
||||
if (r_real_id != NULL) {
|
||||
*r_real_id = real_id;
|
||||
}
|
||||
|
||||
if (path != NULL) {
|
||||
char *new_path = NULL;
|
||||
|
||||
if (real_id) {
|
||||
if (prefix[0]) {
|
||||
new_path = BLI_sprintfN("%s%s%s", prefix, path[0] == '[' ? "" : ".", path);
|
||||
}
|
||||
else {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
MEM_freeN(path);
|
||||
return new_path;
|
||||
}
|
||||
return prefix[0] != '\0' ? BLI_strdup(prefix) : NULL;
|
||||
}
|
||||
|
||||
char *RNA_path_from_ID_to_struct(const PointerRNA *ptr)
|
||||
{
|
||||
char *ptrpath = NULL;
|
||||
|
||||
if (!ptr->owner_id || !ptr->data) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!RNA_struct_is_ID(ptr->type)) {
|
||||
if (ptr->type->path) {
|
||||
/* if type has a path to some ID, use it */
|
||||
ptrpath = ptr->type->path((PointerRNA *)ptr);
|
||||
}
|
||||
else if (ptr->type->nested && RNA_struct_is_ID(ptr->type->nested)) {
|
||||
PointerRNA parentptr;
|
||||
PropertyRNA *userprop;
|
||||
|
||||
/* find the property in the struct we're nested in that references this struct, and
|
||||
* use its identifier as the first part of the path used...
|
||||
*/
|
||||
RNA_id_pointer_create(ptr->owner_id, &parentptr);
|
||||
userprop = RNA_struct_find_nested(&parentptr, ptr->type);
|
||||
|
||||
if (userprop) {
|
||||
ptrpath = BLI_strdup(RNA_property_identifier(userprop));
|
||||
}
|
||||
else {
|
||||
return NULL; /* can't do anything about this case yet... */
|
||||
}
|
||||
}
|
||||
else if (RNA_struct_is_a(ptr->type, &RNA_PropertyGroup)) {
|
||||
/* special case, easier to deal with here than in ptr->type->path() */
|
||||
return rna_path_from_ID_to_idpgroup(ptr);
|
||||
}
|
||||
else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return ptrpath;
|
||||
}
|
||||
|
||||
char *RNA_path_from_real_ID_to_struct(Main *bmain, const PointerRNA *ptr, struct ID **r_real)
|
||||
{
|
||||
char *path = RNA_path_from_ID_to_struct(ptr);
|
||||
|
||||
/* NULL path is valid in that case, when given struct is an ID one... */
|
||||
return rna_prepend_real_ID_path(bmain, ptr->owner_id, path, r_real);
|
||||
}
|
||||
|
||||
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(const 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]);
|
||||
}
|
||||
}
|
||||
|
||||
char *RNA_path_from_ID_to_property_index(const 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->owner_id || !ptr->data) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* path from ID to the struct holding this property */
|
||||
ptrpath = RNA_path_from_ID_to_struct(ptr);
|
||||
|
||||
propname = RNA_property_identifier(prop);
|
||||
|
||||
/* support indexing w/ multi-dimensional arrays */
|
||||
char index_str[RNA_MAX_ARRAY_LENGTH * 12 + 1];
|
||||
if (index_dim == 0) {
|
||||
index_str[0] = '\0';
|
||||
}
|
||||
else {
|
||||
rna_path_array_multi_string_from_flat_index(
|
||||
ptr, prop, index_dim, index, index_str, sizeof(index_str));
|
||||
}
|
||||
|
||||
if (ptrpath) {
|
||||
if (is_rna) {
|
||||
path = BLI_sprintfN("%s.%s%s", ptrpath, propname, index_str);
|
||||
}
|
||||
else {
|
||||
char propname_esc[MAX_IDPROP_NAME * 2];
|
||||
BLI_str_escape(propname_esc, propname, sizeof(propname_esc));
|
||||
path = BLI_sprintfN("%s[\"%s\"]%s", ptrpath, propname_esc, index_str);
|
||||
}
|
||||
MEM_freeN(ptrpath);
|
||||
}
|
||||
else if (RNA_struct_is_ID(ptr->type)) {
|
||||
if (is_rna) {
|
||||
path = BLI_sprintfN("%s%s", propname, index_str);
|
||||
}
|
||||
else {
|
||||
char propname_esc[MAX_IDPROP_NAME * 2];
|
||||
BLI_str_escape(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(const PointerRNA *ptr, PropertyRNA *prop)
|
||||
{
|
||||
return RNA_path_from_ID_to_property_index(ptr, prop, 0, -1);
|
||||
}
|
||||
|
||||
char *RNA_path_from_real_ID_to_property_index(Main *bmain,
|
||||
const PointerRNA *ptr,
|
||||
PropertyRNA *prop,
|
||||
int index_dim,
|
||||
int index,
|
||||
ID **r_real_id)
|
||||
{
|
||||
char *path = RNA_path_from_ID_to_property_index(ptr, prop, index_dim, index);
|
||||
|
||||
/* NULL path is always an error here, in that case do not return the 'fake ID from real ID' part
|
||||
* of the path either. */
|
||||
return path != NULL ? rna_prepend_real_ID_path(bmain, ptr->owner_id, path, r_real_id) : NULL;
|
||||
}
|
||||
|
||||
char *RNA_path_resolve_from_type_to_property(const 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->owner_id, &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;
|
||||
}
|
||||
|
||||
char *RNA_path_full_ID_py(Main *bmain, ID *id)
|
||||
{
|
||||
const char *path;
|
||||
ID *id_real = RNA_find_real_ID_and_path(bmain, id, &path);
|
||||
|
||||
if (id_real) {
|
||||
id = id_real;
|
||||
}
|
||||
else {
|
||||
path = "";
|
||||
}
|
||||
|
||||
char lib_filepath_esc[(sizeof(id->lib->filepath) * 2) + 4];
|
||||
if (ID_IS_LINKED(id)) {
|
||||
int ofs = 0;
|
||||
memcpy(lib_filepath_esc, ", \"", 3);
|
||||
ofs += 3;
|
||||
ofs += BLI_str_escape(lib_filepath_esc + ofs, id->lib->filepath, sizeof(lib_filepath_esc));
|
||||
memcpy(lib_filepath_esc + ofs, "\"", 2);
|
||||
}
|
||||
else {
|
||||
lib_filepath_esc[0] = '\0';
|
||||
}
|
||||
|
||||
char id_esc[(sizeof(id->name) - 2) * 2];
|
||||
BLI_str_escape(id_esc, id->name + 2, sizeof(id_esc));
|
||||
|
||||
return BLI_sprintfN("bpy.data.%s[\"%s\"%s]%s%s",
|
||||
BKE_idtype_idcode_to_name_plural(GS(id->name)),
|
||||
id_esc,
|
||||
lib_filepath_esc,
|
||||
path[0] ? "." : "",
|
||||
path);
|
||||
}
|
||||
|
||||
char *RNA_path_full_struct_py(Main *bmain, const PointerRNA *ptr)
|
||||
{
|
||||
char *id_path;
|
||||
char *data_path;
|
||||
|
||||
char *ret;
|
||||
|
||||
if (!ptr->owner_id) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* never fails */
|
||||
id_path = RNA_path_full_ID_py(bmain, ptr->owner_id);
|
||||
|
||||
data_path = RNA_path_from_ID_to_struct(ptr);
|
||||
|
||||
/* XXX data_path may be NULL (see T36788),
|
||||
* 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;
|
||||
}
|
||||
|
||||
char *RNA_path_full_property_py_ex(
|
||||
Main *bmain, const PointerRNA *ptr, PropertyRNA *prop, int index, bool use_fallback)
|
||||
{
|
||||
char *id_path;
|
||||
const char *data_delim;
|
||||
const char *data_path;
|
||||
bool data_path_free;
|
||||
|
||||
char *ret;
|
||||
|
||||
if (!ptr->owner_id) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* never fails */
|
||||
id_path = RNA_path_full_ID_py(bmain, ptr->owner_id);
|
||||
|
||||
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 ignorance. */
|
||||
data_path = RNA_property_identifier(prop);
|
||||
data_delim = " ... ";
|
||||
}
|
||||
else {
|
||||
data_delim = ".";
|
||||
}
|
||||
data_path_free = false;
|
||||
}
|
||||
|
||||
if ((index == -1) || (RNA_property_array_check(prop) == false)) {
|
||||
ret = BLI_sprintfN("%s%s%s", id_path, data_delim, data_path);
|
||||
}
|
||||
else {
|
||||
ret = BLI_sprintfN("%s%s%s[%d]", id_path, data_delim, data_path, index);
|
||||
}
|
||||
MEM_freeN(id_path);
|
||||
if (data_path_free) {
|
||||
MEM_freeN((void *)data_path);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
char *RNA_path_full_property_py(Main *bmain, const PointerRNA *ptr, PropertyRNA *prop, int index)
|
||||
{
|
||||
return RNA_path_full_property_py_ex(bmain, ptr, prop, index, false);
|
||||
}
|
||||
|
||||
char *RNA_path_struct_property_py(PointerRNA *ptr, PropertyRNA *prop, int index)
|
||||
{
|
||||
char *data_path;
|
||||
|
||||
char *ret;
|
||||
|
||||
if (!ptr->owner_id) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
data_path = RNA_path_from_ID_to_property(ptr, prop);
|
||||
|
||||
if (data_path == NULL) {
|
||||
/* This may not be an ID at all, check for simple when pointer owns property.
|
||||
* TODO: more complex nested case. */
|
||||
if (!RNA_struct_is_ID(ptr->type)) {
|
||||
const char *prop_identifier = RNA_property_identifier(prop);
|
||||
if (RNA_struct_find_property(ptr, prop_identifier) == prop) {
|
||||
data_path = BLI_strdup(prop_identifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((index == -1) || (RNA_property_array_check(prop) == false)) {
|
||||
ret = BLI_strdup(data_path);
|
||||
}
|
||||
else {
|
||||
ret = BLI_sprintfN("%s[%d]", data_path, index);
|
||||
}
|
||||
|
||||
if (data_path) {
|
||||
MEM_freeN(data_path);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
char *RNA_path_property_py(const PointerRNA *UNUSED(ptr), PropertyRNA *prop, int index)
|
||||
{
|
||||
const bool is_rna = (prop->magic == RNA_MAGIC);
|
||||
const char *propname = RNA_property_identifier(prop);
|
||||
char *ret;
|
||||
|
||||
if ((index == -1) || (RNA_property_array_check(prop) == false)) {
|
||||
if (is_rna) {
|
||||
ret = BLI_strdup(propname);
|
||||
}
|
||||
else {
|
||||
char propname_esc[MAX_IDPROP_NAME * 2];
|
||||
BLI_str_escape(propname_esc, propname, sizeof(propname_esc));
|
||||
ret = BLI_sprintfN("[\"%s\"]", propname_esc);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (is_rna) {
|
||||
ret = BLI_sprintfN("%s[%d]", propname, index);
|
||||
}
|
||||
else {
|
||||
char propname_esc[MAX_IDPROP_NAME * 2];
|
||||
BLI_str_escape(propname_esc, propname, sizeof(propname_esc));
|
||||
ret = BLI_sprintfN("[\"%s\"][%d]", propname_esc, index);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Quick name based property access */
|
||||
|
||||
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_define.h"
|
||||
#include "RNA_enum_types.h"
|
||||
#include "RNA_path.h"
|
||||
#include "RNA_prototypes.h"
|
||||
|
||||
#include "rna_access_internal.h"
|
||||
|
||||
@@ -10,6 +10,10 @@
|
||||
|
||||
#include "rna_internal_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct IDProperty;
|
||||
struct PropertyRNAOrID;
|
||||
|
||||
@@ -26,3 +30,9 @@ void rna_property_rna_or_id_get(PropertyRNA *prop,
|
||||
|
||||
void rna_idproperty_touch(struct IDProperty *idprop);
|
||||
struct IDProperty *rna_idproperty_find(PointerRNA *ptr, const char *name);
|
||||
|
||||
PropertyRNA *rna_struct_find_nested(PointerRNA *ptr, StructRNA *srna);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#ifdef RNA_RUNTIME
|
||||
|
||||
# include "RNA_access.h"
|
||||
# include "RNA_path.h"
|
||||
|
||||
# include "DNA_image_types.h"
|
||||
# include "DNA_material_types.h"
|
||||
|
||||
@@ -14,6 +14,10 @@
|
||||
|
||||
#include "UI_resources.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define RNA_MAGIC ((int)~0)
|
||||
|
||||
struct AssetLibraryReference;
|
||||
@@ -691,3 +695,7 @@ void rna_RenderPass_rect_set(PointerRNA *ptr, const float *values);
|
||||
: -FLT_MAX, double \
|
||||
: -DBL_MAX)
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
1360
source/blender/makesrna/intern/rna_path.cc
Normal file
1360
source/blender/makesrna/intern/rna_path.cc
Normal file
@@ -0,0 +1,1360 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup RNA
|
||||
*/
|
||||
|
||||
#include <cstdlib>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "BLI_alloca.h"
|
||||
#include "BLI_dynstr.h"
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "BKE_idprop.h"
|
||||
#include "BKE_idtype.h"
|
||||
|
||||
#include "DNA_ID.h" /* For ID properties. */
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_define.h"
|
||||
#include "RNA_path.h"
|
||||
#include "RNA_prototypes.h"
|
||||
|
||||
#include "rna_access_internal.h"
|
||||
#include "rna_internal.h"
|
||||
|
||||
/**
|
||||
* Extract the first token from `path`.
|
||||
*
|
||||
* \param path: Extract the token from path, step the pointer to the beginning of the next token
|
||||
* \return The nil terminated token.
|
||||
*/
|
||||
static char *rna_path_token(const char **path, char *fixedbuf, int fixedlen)
|
||||
{
|
||||
int len = 0;
|
||||
|
||||
/* Get data until `.` or `[`. */
|
||||
const char *p = *path;
|
||||
while (*p && !ELEM(*p, '.', '[')) {
|
||||
len++;
|
||||
p++;
|
||||
}
|
||||
|
||||
/* Empty, return. */
|
||||
if (UNLIKELY(len == 0)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Try to use fixed buffer if possible. */
|
||||
char *buf = (len + 1 < fixedlen) ? fixedbuf :
|
||||
(char *)MEM_mallocN(sizeof(char) * (len + 1), __func__);
|
||||
memcpy(buf, *path, sizeof(char) * len);
|
||||
buf[len] = '\0';
|
||||
|
||||
if (*p == '.') {
|
||||
p++;
|
||||
}
|
||||
*path = p;
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the first token in brackets from `path` (with quoted text support).
|
||||
*
|
||||
* - `[0]` -> `0`
|
||||
* - `["Some\"Quote"]` -> `Some"Quote`
|
||||
*
|
||||
* \param path: Extract the token from path, step the pointer to the beginning of the next token
|
||||
* (past quoted text and brackets).
|
||||
* \return The nil terminated token.
|
||||
*/
|
||||
static char *rna_path_token_in_brackets(const char **path,
|
||||
char *fixedbuf,
|
||||
int fixedlen,
|
||||
bool *r_quoted)
|
||||
{
|
||||
int len = 0;
|
||||
bool quoted = false;
|
||||
|
||||
BLI_assert(r_quoted != NULL);
|
||||
|
||||
/* Get data between `[]`, check escaping quotes and back-slashes with #BLI_str_unescape. */
|
||||
if (UNLIKELY(**path != '[')) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
(*path)++;
|
||||
const char *p = *path;
|
||||
|
||||
/* 2 kinds of look-ups now, quoted or unquoted. */
|
||||
if (*p == '"') {
|
||||
/* Find the matching quote. */
|
||||
(*path)++;
|
||||
p = *path;
|
||||
const char *p_end = BLI_str_escape_find_quote(p);
|
||||
if (p_end == NULL) {
|
||||
/* No Matching quote. */
|
||||
return NULL;
|
||||
}
|
||||
/* Exclude the last quote from the length. */
|
||||
len += (p_end - p);
|
||||
|
||||
/* Skip the last quoted char to get the `]`. */
|
||||
p_end += 1;
|
||||
p = p_end;
|
||||
quoted = true;
|
||||
}
|
||||
else {
|
||||
/* Find the matching bracket. */
|
||||
while (*p && (*p != ']')) {
|
||||
len++;
|
||||
p++;
|
||||
}
|
||||
}
|
||||
|
||||
if (UNLIKELY(*p != ']')) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Empty, return. */
|
||||
if (UNLIKELY(len == 0)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Try to use fixed buffer if possible. */
|
||||
char *buf = (len + 1 < fixedlen) ? fixedbuf :
|
||||
(char *)MEM_mallocN(sizeof(char) * (len + 1), __func__);
|
||||
|
||||
/* Copy string, taking into account escaped ']' */
|
||||
if (quoted) {
|
||||
BLI_str_unescape(buf, *path, len);
|
||||
/* +1 to step over the last quote. */
|
||||
BLI_assert((*path)[len] == '"');
|
||||
p = (*path) + len + 1;
|
||||
}
|
||||
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;
|
||||
|
||||
*r_quoted = quoted;
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* \return true when the key in the path is correctly parsed and found in the collection
|
||||
* or when the path is empty.
|
||||
*/
|
||||
static bool rna_path_parse_collection_key(const char **path,
|
||||
PointerRNA *ptr,
|
||||
PropertyRNA *prop,
|
||||
PointerRNA *r_nextptr)
|
||||
{
|
||||
char fixedbuf[256];
|
||||
int intkey;
|
||||
|
||||
*r_nextptr = *ptr;
|
||||
|
||||
/* end of path, ok */
|
||||
if (!(**path)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool found = false;
|
||||
if (**path == '[') {
|
||||
bool quoted;
|
||||
char *token;
|
||||
|
||||
/* resolve the lookup with [] brackets */
|
||||
token = rna_path_token_in_brackets(path, fixedbuf, sizeof(fixedbuf), "ed);
|
||||
|
||||
if (!token) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* check for "" to see if it is a string */
|
||||
if (quoted) {
|
||||
if (RNA_property_collection_lookup_string(ptr, prop, token, r_nextptr)) {
|
||||
found = true;
|
||||
}
|
||||
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)) {
|
||||
found = true;
|
||||
}
|
||||
else {
|
||||
r_nextptr->data = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (token != fixedbuf) {
|
||||
MEM_freeN(token);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (RNA_property_collection_type_get(ptr, prop, r_nextptr)) {
|
||||
found = true;
|
||||
}
|
||||
else {
|
||||
/* ensure we quit on invalid values */
|
||||
r_nextptr->data = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
static bool rna_path_parse_array_index(const char **path,
|
||||
PointerRNA *ptr,
|
||||
PropertyRNA *prop,
|
||||
int *r_index)
|
||||
{
|
||||
char fixedbuf[256];
|
||||
int index_arr[RNA_MAX_ARRAY_DIMENSION] = {0};
|
||||
int len[RNA_MAX_ARRAY_DIMENSION];
|
||||
const int dim = RNA_property_array_dimension(ptr, prop, len);
|
||||
int i;
|
||||
|
||||
*r_index = -1;
|
||||
|
||||
/* end of path, ok */
|
||||
if (!(**path)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (i = 0; i < dim; i++) {
|
||||
int temp_index = -1;
|
||||
char *token;
|
||||
|
||||
/* multi index resolve */
|
||||
if (**path == '[') {
|
||||
bool quoted;
|
||||
token = rna_path_token_in_brackets(path, fixedbuf, sizeof(fixedbuf), "ed);
|
||||
|
||||
if (token == NULL) {
|
||||
/* invalid syntax blah[] */
|
||||
return false;
|
||||
}
|
||||
/* check for "" to see if it is a string */
|
||||
if (quoted) {
|
||||
temp_index = RNA_property_array_item_index(prop, *token);
|
||||
}
|
||||
else {
|
||||
/* otherwise do int lookup */
|
||||
temp_index = atoi(token);
|
||||
|
||||
if (temp_index == 0 && (token[0] != '0' || token[1] != '\0')) {
|
||||
if (token != fixedbuf) {
|
||||
MEM_freeN(token);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (dim == 1) {
|
||||
/* location.x || scale.X, single dimension arrays only */
|
||||
token = rna_path_token(path, fixedbuf, sizeof(fixedbuf));
|
||||
if (token == NULL) {
|
||||
/* invalid syntax blah. */
|
||||
return false;
|
||||
}
|
||||
temp_index = RNA_property_array_item_index(prop, *token);
|
||||
}
|
||||
else {
|
||||
/* just to avoid uninitialized pointer use */
|
||||
token = fixedbuf;
|
||||
}
|
||||
|
||||
if (token != fixedbuf) {
|
||||
MEM_freeN(token);
|
||||
}
|
||||
|
||||
/* out of range */
|
||||
if (temp_index < 0 || temp_index >= len[i]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
index_arr[i] = temp_index;
|
||||
/* end multi index resolve */
|
||||
}
|
||||
|
||||
/* arrays always contain numbers so further values are not valid */
|
||||
if (**path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* flatten index over all dimensions */
|
||||
{
|
||||
int totdim = 1;
|
||||
int flat_index = 0;
|
||||
|
||||
for (i = dim - 1; i >= 0; i--) {
|
||||
flat_index += index_arr[i] * totdim;
|
||||
totdim *= len[i];
|
||||
}
|
||||
|
||||
*r_index = flat_index;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic rna path parser.
|
||||
*
|
||||
* \note All parameters besides \a ptr and \a path are optional.
|
||||
*
|
||||
* \param ptr: The root of given RNA path.
|
||||
* \param path: The RNA path.
|
||||
* \param r_ptr: The final RNA data holding the last property in \a path.
|
||||
* \param r_prop: The final property of \a r_ptr, from \a path.
|
||||
* \param r_index: The final index in the \a r_prop, if defined by \a path.
|
||||
* \param r_item_ptr: Only valid for Pointer and Collection, return the actual value of the
|
||||
* pointer, or of the collection item.
|
||||
* Mutually exclusive with \a eval_pointer option.
|
||||
* \param r_elements: A list of \a PropertyElemRNA items(pairs of \a PointerRNA, \a PropertyRNA
|
||||
* that represent the whole given \a path).
|
||||
* \param eval_pointer: If \a true, and \a path leads to a Pointer property, or an item in a
|
||||
* Collection property, \a r_ptr will be set to the value of that property,
|
||||
* and \a r_prop will be NULL.
|
||||
* Mutually exclusive with \a r_item_ptr.
|
||||
*
|
||||
* \return \a true on success, \a false if the path is somehow invalid.
|
||||
*/
|
||||
static bool rna_path_parse(const 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);
|
||||
}
|
||||
|
||||
const bool use_id_prop = (*path == '[');
|
||||
/* custom property lookup ?
|
||||
* C.object["someprop"]
|
||||
*/
|
||||
|
||||
if (!curptr.data) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* look up property name in current struct */
|
||||
bool quoted = false;
|
||||
char *token = use_id_prop ?
|
||||
rna_path_token_in_brackets(&path, fixedbuf, sizeof(fixedbuf), "ed) :
|
||||
rna_path_token(&path, fixedbuf, sizeof(fixedbuf));
|
||||
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 && quoted) {
|
||||
prop = (PropertyRNA *)IDP_GetPropertyFromGroup(group, token);
|
||||
}
|
||||
}
|
||||
else {
|
||||
prop = RNA_struct_find_property(&curptr, token);
|
||||
}
|
||||
|
||||
if (token != fixedbuf) {
|
||||
MEM_freeN(token);
|
||||
}
|
||||
|
||||
if (!prop) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (r_elements) {
|
||||
prop_elem = MEM_cnew<PropertyElemRNA>(__func__);
|
||||
prop_elem->ptr = curptr;
|
||||
prop_elem->prop = prop;
|
||||
prop_elem->index = -1; /* index will be added later, if needed. */
|
||||
BLI_addtail(r_elements, prop_elem);
|
||||
}
|
||||
|
||||
type = RNA_property_type(prop);
|
||||
|
||||
/* now look up the value of this property if it is a pointer or
|
||||
* collection, otherwise return the property rna so that the
|
||||
* caller can read the value of the property itself */
|
||||
switch (type) {
|
||||
case PROP_POINTER: {
|
||||
/* resolve pointer if further path elements follow
|
||||
* or explicitly requested
|
||||
*/
|
||||
if (do_item_ptr || eval_pointer || *path != '\0') {
|
||||
nextptr = RNA_property_pointer_get(&curptr, prop);
|
||||
}
|
||||
|
||||
if (eval_pointer || *path != '\0') {
|
||||
curptr = nextptr;
|
||||
prop = NULL; /* now we have a PointerRNA, the prop is our parent so forget it */
|
||||
index = -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PROP_COLLECTION: {
|
||||
/* Resolve pointer if further path elements follow.
|
||||
* Note that if path is empty, rna_path_parse_collection_key will do nothing anyway,
|
||||
* so do_item_ptr is of no use in that case.
|
||||
*/
|
||||
if (*path) {
|
||||
if (!rna_path_parse_collection_key(&path, &curptr, prop, &nextptr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (eval_pointer || *path != '\0') {
|
||||
curptr = nextptr;
|
||||
prop = NULL; /* now we have a PointerRNA, the prop is our parent so forget it */
|
||||
index = -1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
if (r_index || prop_elem) {
|
||||
if (!rna_path_parse_array_index(&path, &curptr, prop, &index)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (prop_elem) {
|
||||
prop_elem->index = index;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (r_ptr) {
|
||||
*r_ptr = curptr;
|
||||
}
|
||||
if (r_prop) {
|
||||
*r_prop = prop;
|
||||
}
|
||||
if (r_index) {
|
||||
*r_index = index;
|
||||
}
|
||||
if (r_item_ptr && do_item_ptr) {
|
||||
*r_item_ptr = nextptr;
|
||||
}
|
||||
|
||||
if (prop_elem && (prop_elem->ptr.data != curptr.data || prop_elem->prop != prop ||
|
||||
prop_elem->index != index)) {
|
||||
prop_elem = MEM_cnew<PropertyElemRNA>(__func__);
|
||||
prop_elem->ptr = curptr;
|
||||
prop_elem->prop = prop;
|
||||
prop_elem->index = index;
|
||||
BLI_addtail(r_elements, prop_elem);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RNA_path_resolve(const 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;
|
||||
}
|
||||
|
||||
bool RNA_path_resolve_full(
|
||||
const 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;
|
||||
}
|
||||
|
||||
bool RNA_path_resolve_full_maybe_null(
|
||||
const PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop, int *r_index)
|
||||
{
|
||||
return rna_path_parse(ptr, path, r_ptr, r_prop, r_index, NULL, NULL, true);
|
||||
}
|
||||
|
||||
bool RNA_path_resolve_property(const 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;
|
||||
}
|
||||
|
||||
bool RNA_path_resolve_property_full(
|
||||
const 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;
|
||||
}
|
||||
|
||||
bool RNA_path_resolve_property_and_item_pointer(const 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;
|
||||
}
|
||||
|
||||
bool RNA_path_resolve_property_and_item_pointer_full(const 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;
|
||||
}
|
||||
bool RNA_path_resolve_elements(PointerRNA *ptr, const char *path, ListBase *r_elements)
|
||||
{
|
||||
return rna_path_parse(ptr, path, NULL, NULL, NULL, NULL, r_elements, false);
|
||||
}
|
||||
|
||||
char *RNA_path_append(const char *path,
|
||||
const PointerRNA *UNUSED(ptr),
|
||||
PropertyRNA *prop,
|
||||
int intkey,
|
||||
const char *strkey)
|
||||
{
|
||||
DynStr *dynstr;
|
||||
char *result;
|
||||
|
||||
dynstr = BLI_dynstr_new();
|
||||
|
||||
/* add .identifier */
|
||||
if (path) {
|
||||
BLI_dynstr_append(dynstr, path);
|
||||
if (*path) {
|
||||
BLI_dynstr_append(dynstr, ".");
|
||||
}
|
||||
}
|
||||
|
||||
BLI_dynstr_append(dynstr, RNA_property_identifier(prop));
|
||||
|
||||
if (RNA_property_type(prop) == PROP_COLLECTION) {
|
||||
/* add ["strkey"] or [intkey] */
|
||||
BLI_dynstr_append(dynstr, "[");
|
||||
|
||||
if (strkey) {
|
||||
const int strkey_esc_max_size = (strlen(strkey) * 2) + 1;
|
||||
char *strkey_esc = BLI_array_alloca(strkey_esc, strkey_esc_max_size);
|
||||
BLI_str_escape(strkey_esc, strkey, strkey_esc_max_size);
|
||||
BLI_dynstr_append(dynstr, "\"");
|
||||
BLI_dynstr_append(dynstr, strkey_esc);
|
||||
BLI_dynstr_append(dynstr, "\"");
|
||||
}
|
||||
else {
|
||||
char appendstr[128];
|
||||
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;
|
||||
}
|
||||
|
||||
/* Having both path append & back seems like it could be useful,
|
||||
* this function isn't used at the moment. */
|
||||
static UNUSED_FUNCTION_WITH_RETURN_TYPE(char *, RNA_path_back)(const char *path)
|
||||
{
|
||||
char fixedbuf[256];
|
||||
const char *previous, *current;
|
||||
char *result;
|
||||
int i;
|
||||
|
||||
if (!path) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
previous = NULL;
|
||||
current = path;
|
||||
|
||||
/* parse token by token until the end, then we back up to the previous
|
||||
* position and strip of the next token to get the path one step back */
|
||||
while (*current) {
|
||||
char *token;
|
||||
|
||||
token = rna_path_token(¤t, fixedbuf, sizeof(fixedbuf));
|
||||
|
||||
if (!token) {
|
||||
return NULL;
|
||||
}
|
||||
if (token != fixedbuf) {
|
||||
MEM_freeN(token);
|
||||
}
|
||||
|
||||
/* in case of collection we also need to strip off [] */
|
||||
bool quoted;
|
||||
token = rna_path_token_in_brackets(¤t, fixedbuf, sizeof(fixedbuf), "ed);
|
||||
if (token && token != fixedbuf) {
|
||||
MEM_freeN(token);
|
||||
}
|
||||
|
||||
if (!*current) {
|
||||
break;
|
||||
}
|
||||
|
||||
previous = current;
|
||||
}
|
||||
|
||||
if (!previous) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* copy and strip off last token */
|
||||
i = previous - path;
|
||||
result = BLI_strdup(path);
|
||||
|
||||
if (i > 0 && result[i - 1] == '.') {
|
||||
i--;
|
||||
}
|
||||
result[i] = 0;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const char *RNA_path_array_index_token_find(const char *rna_path, const PropertyRNA *array_prop)
|
||||
{
|
||||
if (array_prop != NULL) {
|
||||
if (!ELEM(array_prop->type, PROP_BOOLEAN, PROP_INT, PROP_FLOAT)) {
|
||||
BLI_assert(array_prop->arraydimension == 0);
|
||||
return NULL;
|
||||
}
|
||||
if (array_prop->arraydimension == 0) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Valid 'array part' of a rna path can only have '[', ']' and digit characters.
|
||||
* It may have more than one of those (e.g. `[12][1]`) in case of multi-dimensional arrays. */
|
||||
off_t rna_path_len = (off_t)strlen(rna_path);
|
||||
if (rna_path[rna_path_len] != ']') {
|
||||
return NULL;
|
||||
}
|
||||
const char *last_valid_index_token_start = NULL;
|
||||
for (rna_path_len--; rna_path_len >= 0; rna_path_len--) {
|
||||
switch (rna_path[rna_path_len]) {
|
||||
case '[':
|
||||
if (rna_path_len <= 0 || rna_path[rna_path_len - 1] != ']') {
|
||||
return &rna_path[rna_path_len];
|
||||
}
|
||||
last_valid_index_token_start = &rna_path[rna_path_len];
|
||||
rna_path_len--;
|
||||
break;
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
break;
|
||||
default:
|
||||
return last_valid_index_token_start;
|
||||
}
|
||||
}
|
||||
return last_valid_index_token_start;
|
||||
}
|
||||
|
||||
/* generic path search func
|
||||
* if its needed this could also reference the IDProperty direct */
|
||||
typedef struct IDP_Chain {
|
||||
struct IDP_Chain *up; /* parent member, reverse and set to child for path conversion. */
|
||||
|
||||
const char *name;
|
||||
int index;
|
||||
|
||||
} IDP_Chain;
|
||||
|
||||
static char *rna_idp_path_create(IDP_Chain *child_link)
|
||||
{
|
||||
DynStr *dynstr = BLI_dynstr_new();
|
||||
char *path;
|
||||
bool is_first = true;
|
||||
|
||||
int tot = 0;
|
||||
IDP_Chain *link = child_link;
|
||||
|
||||
/* reverse the list */
|
||||
IDP_Chain *link_prev;
|
||||
link_prev = NULL;
|
||||
while (link) {
|
||||
IDP_Chain *link_next = link->up;
|
||||
link->up = link_prev;
|
||||
link_prev = link;
|
||||
link = link_next;
|
||||
tot++;
|
||||
}
|
||||
|
||||
for (link = link_prev; link; link = link->up) {
|
||||
/* pass */
|
||||
if (link->index >= 0) {
|
||||
BLI_dynstr_appendf(dynstr, is_first ? "%s[%d]" : ".%s[%d]", link->name, link->index);
|
||||
}
|
||||
else {
|
||||
BLI_dynstr_appendf(dynstr, is_first ? "%s" : ".%s", link->name);
|
||||
}
|
||||
|
||||
is_first = false;
|
||||
}
|
||||
|
||||
path = BLI_dynstr_get_cstring(dynstr);
|
||||
BLI_dynstr_free(dynstr);
|
||||
|
||||
if (*path == '\0') {
|
||||
MEM_freeN(path);
|
||||
path = NULL;
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
static char *rna_idp_path(PointerRNA *ptr,
|
||||
IDProperty *haystack,
|
||||
IDProperty *needle,
|
||||
IDP_Chain *parent_link)
|
||||
{
|
||||
char *path = NULL;
|
||||
IDP_Chain link;
|
||||
|
||||
IDProperty *iter;
|
||||
int i;
|
||||
|
||||
BLI_assert(haystack->type == IDP_GROUP);
|
||||
|
||||
link.up = parent_link;
|
||||
/* Always set both name and index, else a stale value might get used. */
|
||||
link.name = NULL;
|
||||
link.index = -1;
|
||||
|
||||
for (i = 0, iter = reinterpret_cast<IDProperty *>(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;
|
||||
}
|
||||
|
||||
/* Early out in case the IDProperty type cannot contain RNA properties. */
|
||||
if (!ELEM(iter->type, IDP_GROUP, IDP_IDPARRAY)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Ensure this is RNA. */
|
||||
/* NOTE: `iter` might be a fully user-defined IDProperty (a.k.a. custom data), which name
|
||||
* collides with an actual fully static RNA property of the same struct (which would then not
|
||||
* be flagged with `PROP_IDPROPERTY`).
|
||||
*
|
||||
* That case must be ignored here, we only want to deal with runtime RNA properties stored in
|
||||
* IDProps.
|
||||
*
|
||||
* See T84091. */
|
||||
PropertyRNA *prop = RNA_struct_find_property(ptr, iter->name);
|
||||
if (prop == NULL || (prop->flag & PROP_IDPROPERTY) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (iter->type == IDP_GROUP) {
|
||||
if (prop->type == PROP_POINTER) {
|
||||
PointerRNA child_ptr = RNA_property_pointer_get(ptr, prop);
|
||||
if (RNA_pointer_is_null(&child_ptr)) {
|
||||
/* Pointer ID prop might be a 'leaf' in the IDProp group hierarchy, in which case a NULL
|
||||
* value is perfectly valid. Just means it won't match the searched needle. */
|
||||
continue;
|
||||
}
|
||||
|
||||
link.name = iter->name;
|
||||
link.index = -1;
|
||||
if ((path = rna_idp_path(&child_ptr, iter, needle, &link))) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (iter->type == IDP_IDPARRAY) {
|
||||
if (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;
|
||||
}
|
||||
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)) {
|
||||
if (RNA_pointer_is_null(&child_ptr)) {
|
||||
/* Array item ID prop might be a 'leaf' in the IDProp group hierarchy, in which case
|
||||
* a NULL value is perfectly valid. Just means it won't match the searched needle. */
|
||||
continue;
|
||||
}
|
||||
link.index = j;
|
||||
if ((path = rna_idp_path(&child_ptr, array, needle, &link))) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (path) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
char *RNA_path_from_struct_to_idproperty(PointerRNA *ptr, IDProperty *needle)
|
||||
{
|
||||
IDProperty *haystack = RNA_struct_idprops(ptr, false);
|
||||
|
||||
if (haystack) { /* can fail when called on bones */
|
||||
return rna_idp_path(ptr, haystack, needle, NULL);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static char *rna_path_from_ID_to_idpgroup(const PointerRNA *ptr)
|
||||
{
|
||||
PointerRNA id_ptr;
|
||||
|
||||
BLI_assert(ptr->owner_id != NULL);
|
||||
|
||||
/* TODO: Support Bones/PoseBones. no pointers stored to the bones from here, only the ID.
|
||||
* See example in T25746.
|
||||
* 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->owner_id, &id_ptr);
|
||||
|
||||
return RNA_path_from_struct_to_idproperty(&id_ptr, reinterpret_cast<IDProperty *>(ptr->data));
|
||||
}
|
||||
|
||||
ID *RNA_find_real_ID_and_path(Main *bmain, ID *id, const char **r_path)
|
||||
{
|
||||
if (r_path) {
|
||||
*r_path = "";
|
||||
}
|
||||
|
||||
if ((id == NULL) || (id->flag & LIB_EMBEDDED_DATA) == 0) {
|
||||
return id;
|
||||
}
|
||||
|
||||
const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id);
|
||||
if (r_path) {
|
||||
switch (GS(id->name)) {
|
||||
case ID_NT:
|
||||
*r_path = "node_tree";
|
||||
break;
|
||||
case ID_GR:
|
||||
*r_path = "collection";
|
||||
break;
|
||||
default:
|
||||
BLI_assert_msg(0, "Missing handling of embedded id type.");
|
||||
}
|
||||
}
|
||||
|
||||
if (id_type->owner_get == NULL) {
|
||||
BLI_assert_msg(0, "Missing handling of embedded id type.");
|
||||
return id;
|
||||
}
|
||||
return id_type->owner_get(bmain, id);
|
||||
}
|
||||
|
||||
static char *rna_prepend_real_ID_path(Main *bmain, ID *id, char *path, ID **r_real_id)
|
||||
{
|
||||
if (r_real_id != NULL) {
|
||||
*r_real_id = NULL;
|
||||
}
|
||||
|
||||
const char *prefix;
|
||||
ID *real_id = RNA_find_real_ID_and_path(bmain, id, &prefix);
|
||||
|
||||
if (r_real_id != NULL) {
|
||||
*r_real_id = real_id;
|
||||
}
|
||||
|
||||
if (path != NULL) {
|
||||
char *new_path = NULL;
|
||||
|
||||
if (real_id) {
|
||||
if (prefix[0]) {
|
||||
new_path = BLI_sprintfN("%s%s%s", prefix, path[0] == '[' ? "" : ".", path);
|
||||
}
|
||||
else {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
MEM_freeN(path);
|
||||
return new_path;
|
||||
}
|
||||
return prefix[0] != '\0' ? BLI_strdup(prefix) : NULL;
|
||||
}
|
||||
|
||||
char *RNA_path_from_ID_to_struct(const PointerRNA *ptr)
|
||||
{
|
||||
char *ptrpath = NULL;
|
||||
|
||||
if (!ptr->owner_id || !ptr->data) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!RNA_struct_is_ID(ptr->type)) {
|
||||
if (ptr->type->path) {
|
||||
/* if type has a path to some ID, use it */
|
||||
ptrpath = ptr->type->path((PointerRNA *)ptr);
|
||||
}
|
||||
else if (ptr->type->nested && RNA_struct_is_ID(ptr->type->nested)) {
|
||||
PointerRNA parentptr;
|
||||
PropertyRNA *userprop;
|
||||
|
||||
/* find the property in the struct we're nested in that references this struct, and
|
||||
* use its identifier as the first part of the path used...
|
||||
*/
|
||||
RNA_id_pointer_create(ptr->owner_id, &parentptr);
|
||||
userprop = rna_struct_find_nested(&parentptr, ptr->type);
|
||||
|
||||
if (userprop) {
|
||||
ptrpath = BLI_strdup(RNA_property_identifier(userprop));
|
||||
}
|
||||
else {
|
||||
return NULL; /* can't do anything about this case yet... */
|
||||
}
|
||||
}
|
||||
else if (RNA_struct_is_a(ptr->type, &RNA_PropertyGroup)) {
|
||||
/* special case, easier to deal with here than in ptr->type->path() */
|
||||
return rna_path_from_ID_to_idpgroup(ptr);
|
||||
}
|
||||
else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return ptrpath;
|
||||
}
|
||||
|
||||
char *RNA_path_from_real_ID_to_struct(Main *bmain, const PointerRNA *ptr, struct ID **r_real)
|
||||
{
|
||||
char *path = RNA_path_from_ID_to_struct(ptr);
|
||||
|
||||
/* NULL path is valid in that case, when given struct is an ID one... */
|
||||
return rna_prepend_real_ID_path(bmain, ptr->owner_id, path, r_real);
|
||||
}
|
||||
|
||||
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(const 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]);
|
||||
}
|
||||
}
|
||||
|
||||
char *RNA_path_from_ID_to_property_index(const 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->owner_id || !ptr->data) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* path from ID to the struct holding this property */
|
||||
ptrpath = RNA_path_from_ID_to_struct(ptr);
|
||||
|
||||
propname = RNA_property_identifier(prop);
|
||||
|
||||
/* support indexing w/ multi-dimensional arrays */
|
||||
char index_str[RNA_MAX_ARRAY_LENGTH * 12 + 1];
|
||||
if (index_dim == 0) {
|
||||
index_str[0] = '\0';
|
||||
}
|
||||
else {
|
||||
rna_path_array_multi_string_from_flat_index(
|
||||
ptr, prop, index_dim, index, index_str, sizeof(index_str));
|
||||
}
|
||||
|
||||
if (ptrpath) {
|
||||
if (is_rna) {
|
||||
path = BLI_sprintfN("%s.%s%s", ptrpath, propname, index_str);
|
||||
}
|
||||
else {
|
||||
char propname_esc[MAX_IDPROP_NAME * 2];
|
||||
BLI_str_escape(propname_esc, propname, sizeof(propname_esc));
|
||||
path = BLI_sprintfN("%s[\"%s\"]%s", ptrpath, propname_esc, index_str);
|
||||
}
|
||||
MEM_freeN(ptrpath);
|
||||
}
|
||||
else if (RNA_struct_is_ID(ptr->type)) {
|
||||
if (is_rna) {
|
||||
path = BLI_sprintfN("%s%s", propname, index_str);
|
||||
}
|
||||
else {
|
||||
char propname_esc[MAX_IDPROP_NAME * 2];
|
||||
BLI_str_escape(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(const PointerRNA *ptr, PropertyRNA *prop)
|
||||
{
|
||||
return RNA_path_from_ID_to_property_index(ptr, prop, 0, -1);
|
||||
}
|
||||
|
||||
char *RNA_path_from_real_ID_to_property_index(Main *bmain,
|
||||
const PointerRNA *ptr,
|
||||
PropertyRNA *prop,
|
||||
int index_dim,
|
||||
int index,
|
||||
ID **r_real_id)
|
||||
{
|
||||
char *path = RNA_path_from_ID_to_property_index(ptr, prop, index_dim, index);
|
||||
|
||||
/* NULL path is always an error here, in that case do not return the 'fake ID from real ID' part
|
||||
* of the path either. */
|
||||
return path != NULL ? rna_prepend_real_ID_path(bmain, ptr->owner_id, path, r_real_id) : NULL;
|
||||
}
|
||||
|
||||
char *RNA_path_resolve_from_type_to_property(const 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->owner_id, &idptr);
|
||||
|
||||
if (RNA_path_resolve_elements(&idptr, full_path, &path_elems)) {
|
||||
LISTBASE_FOREACH_BACKWARD (PropertyElemRNA *, prop_elem, &path_elems) {
|
||||
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;
|
||||
}
|
||||
|
||||
char *RNA_path_full_ID_py(Main *bmain, ID *id)
|
||||
{
|
||||
const char *path;
|
||||
ID *id_real = RNA_find_real_ID_and_path(bmain, id, &path);
|
||||
|
||||
if (id_real) {
|
||||
id = id_real;
|
||||
}
|
||||
else {
|
||||
path = "";
|
||||
}
|
||||
|
||||
char lib_filepath_esc[(sizeof(id->lib->filepath) * 2) + 4];
|
||||
if (ID_IS_LINKED(id)) {
|
||||
int ofs = 0;
|
||||
memcpy(lib_filepath_esc, ", \"", 3);
|
||||
ofs += 3;
|
||||
ofs += BLI_str_escape(lib_filepath_esc + ofs, id->lib->filepath, sizeof(lib_filepath_esc));
|
||||
memcpy(lib_filepath_esc + ofs, "\"", 2);
|
||||
}
|
||||
else {
|
||||
lib_filepath_esc[0] = '\0';
|
||||
}
|
||||
|
||||
char id_esc[(sizeof(id->name) - 2) * 2];
|
||||
BLI_str_escape(id_esc, id->name + 2, sizeof(id_esc));
|
||||
|
||||
return BLI_sprintfN("bpy.data.%s[\"%s\"%s]%s%s",
|
||||
BKE_idtype_idcode_to_name_plural(GS(id->name)),
|
||||
id_esc,
|
||||
lib_filepath_esc,
|
||||
path[0] ? "." : "",
|
||||
path);
|
||||
}
|
||||
|
||||
char *RNA_path_full_struct_py(Main *bmain, const PointerRNA *ptr)
|
||||
{
|
||||
char *id_path;
|
||||
char *data_path;
|
||||
|
||||
char *ret;
|
||||
|
||||
if (!ptr->owner_id) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* never fails */
|
||||
id_path = RNA_path_full_ID_py(bmain, ptr->owner_id);
|
||||
|
||||
data_path = RNA_path_from_ID_to_struct(ptr);
|
||||
|
||||
/* XXX data_path may be NULL (see T36788),
|
||||
* 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;
|
||||
}
|
||||
|
||||
char *RNA_path_full_property_py_ex(
|
||||
Main *bmain, const PointerRNA *ptr, PropertyRNA *prop, int index, bool use_fallback)
|
||||
{
|
||||
char *id_path;
|
||||
const char *data_delim;
|
||||
const char *data_path;
|
||||
bool data_path_free;
|
||||
|
||||
char *ret;
|
||||
|
||||
if (!ptr->owner_id) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* never fails */
|
||||
id_path = RNA_path_full_ID_py(bmain, ptr->owner_id);
|
||||
|
||||
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 ignorance. */
|
||||
data_path = RNA_property_identifier(prop);
|
||||
data_delim = " ... ";
|
||||
}
|
||||
else {
|
||||
data_delim = ".";
|
||||
}
|
||||
data_path_free = false;
|
||||
}
|
||||
|
||||
if ((index == -1) || (RNA_property_array_check(prop) == false)) {
|
||||
ret = BLI_sprintfN("%s%s%s", id_path, data_delim, data_path);
|
||||
}
|
||||
else {
|
||||
ret = BLI_sprintfN("%s%s%s[%d]", id_path, data_delim, data_path, index);
|
||||
}
|
||||
MEM_freeN(id_path);
|
||||
if (data_path_free) {
|
||||
MEM_freeN((void *)data_path);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
char *RNA_path_full_property_py(Main *bmain, const PointerRNA *ptr, PropertyRNA *prop, int index)
|
||||
{
|
||||
return RNA_path_full_property_py_ex(bmain, ptr, prop, index, false);
|
||||
}
|
||||
|
||||
char *RNA_path_struct_property_py(PointerRNA *ptr, PropertyRNA *prop, int index)
|
||||
{
|
||||
char *data_path;
|
||||
|
||||
char *ret;
|
||||
|
||||
if (!ptr->owner_id) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
data_path = RNA_path_from_ID_to_property(ptr, prop);
|
||||
|
||||
if (data_path == NULL) {
|
||||
/* This may not be an ID at all, check for simple when pointer owns property.
|
||||
* TODO: more complex nested case. */
|
||||
if (!RNA_struct_is_ID(ptr->type)) {
|
||||
const char *prop_identifier = RNA_property_identifier(prop);
|
||||
if (RNA_struct_find_property(ptr, prop_identifier) == prop) {
|
||||
data_path = BLI_strdup(prop_identifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((index == -1) || (RNA_property_array_check(prop) == false)) {
|
||||
ret = BLI_strdup(data_path);
|
||||
}
|
||||
else {
|
||||
ret = BLI_sprintfN("%s[%d]", data_path, index);
|
||||
}
|
||||
|
||||
if (data_path) {
|
||||
MEM_freeN(data_path);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
char *RNA_path_property_py(const PointerRNA *UNUSED(ptr), PropertyRNA *prop, int index)
|
||||
{
|
||||
const bool is_rna = (prop->magic == RNA_MAGIC);
|
||||
const char *propname = RNA_property_identifier(prop);
|
||||
char *ret;
|
||||
|
||||
if ((index == -1) || (RNA_property_array_check(prop) == false)) {
|
||||
if (is_rna) {
|
||||
ret = BLI_strdup(propname);
|
||||
}
|
||||
else {
|
||||
char propname_esc[MAX_IDPROP_NAME * 2];
|
||||
BLI_str_escape(propname_esc, propname, sizeof(propname_esc));
|
||||
ret = BLI_sprintfN("[\"%s\"]", propname_esc);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (is_rna) {
|
||||
ret = BLI_sprintfN("%s[%d]", propname, index);
|
||||
}
|
||||
else {
|
||||
char propname_esc[MAX_IDPROP_NAME * 2];
|
||||
BLI_str_escape(propname_esc, propname, sizeof(propname_esc));
|
||||
ret = BLI_sprintfN("[\"%s\"][%d]", propname_esc, index);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <float.h> /* FLT_MIN/MAX */
|
||||
#include <stddef.h>
|
||||
|
||||
#include "RNA_path.h"
|
||||
#include "RNA_types.h"
|
||||
|
||||
#include "BLI_bitmap.h"
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_enum_types.h"
|
||||
#include "RNA_path.h"
|
||||
#include "RNA_prototypes.h"
|
||||
|
||||
#include "WM_api.h"
|
||||
|
||||
@@ -81,6 +81,7 @@
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_define.h"
|
||||
#include "RNA_enum_types.h"
|
||||
#include "RNA_path.h"
|
||||
#include "RNA_prototypes.h"
|
||||
|
||||
#include "UI_interface.h"
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "message_bus/intern/wm_message_bus_intern.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_path.h"
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Internal Utilities
|
||||
|
||||
Reference in New Issue
Block a user