diff options
author | Julian Eisel <julian@blender.org> | 2022-07-29 17:56:48 +0300 |
---|---|---|
committer | Julian Eisel <julian@blender.org> | 2022-07-29 17:56:48 +0300 |
commit | 42ccbb7cd1d5c68ff0729f43dd3d89b6365b2411 (patch) | |
tree | f4dbbdec81a8b5346f499c6d8c422294a131d0fa /source/blender | |
parent | 187d90f03629edd41aca261a7dedae54a8602ad0 (diff) |
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
Diffstat (limited to 'source/blender')
48 files changed, 1685 insertions, 1574 deletions
diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c index 3ea595cfaf0..c16d19588ed 100644 --- a/source/blender/blenkernel/intern/action.c +++ b/source/blender/blenkernel/intern/action.c @@ -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" diff --git a/source/blender/blenkernel/intern/anim_data.c b/source/blender/blenkernel/intern/anim_data.c index 861a89ea9d7..b5b00e031b2 100644 --- a/source/blender/blenkernel/intern/anim_data.c +++ b/source/blender/blenkernel/intern/anim_data.c @@ -43,6 +43,7 @@ #include "BLO_read_write.h" #include "RNA_access.h" +#include "RNA_path.h" #include "CLG_log.h" diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c index eb4784bebff..19fef1ce825 100644 --- a/source/blender/blenkernel/intern/anim_sys.c +++ b/source/blender/blenkernel/intern/anim_sys.c @@ -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" diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c index 972ff377519..f5876e48241 100644 --- a/source/blender/blenkernel/intern/fcurve.c +++ b/source/blender/blenkernel/intern/fcurve.c @@ -37,6 +37,7 @@ #include "BLO_read_write.h" #include "RNA_access.h" +#include "RNA_path.h" #include "CLG_log.h" diff --git a/source/blender/blenkernel/intern/fcurve_driver.c b/source/blender/blenkernel/intern/fcurve_driver.c index 5d54c5c039b..aa33bef998f 100644 --- a/source/blender/blenkernel/intern/fcurve_driver.c +++ b/source/blender/blenkernel/intern/fcurve_driver.c @@ -30,6 +30,7 @@ #include "BKE_object.h" #include "RNA_access.h" +#include "RNA_path.h" #include "atomic_ops.h" diff --git a/source/blender/blenkernel/intern/key.c b/source/blender/blenkernel/intern/key.c index 7ef15912567..07ce4e46e9b 100644 --- a/source/blender/blenkernel/intern/key.c +++ b/source/blender/blenkernel/intern/key.c @@ -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" diff --git a/source/blender/blenkernel/intern/lib_override.cc b/source/blender/blenkernel/intern/lib_override.cc index 4ad8d26cd2a..6afbb9064a8 100644 --- a/source/blender/blenkernel/intern/lib_override.cc +++ b/source/blender/blenkernel/intern/lib_override.cc @@ -52,6 +52,7 @@ #include "PIL_time.h" #include "RNA_access.h" +#include "RNA_path.h" #include "RNA_prototypes.h" #include "RNA_types.h" diff --git a/source/blender/depsgraph/intern/builder/deg_builder_cache.cc b/source/blender/depsgraph/intern/builder/deg_builder_cache.cc index 6474f853390..129e0093d11 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_cache.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_cache.cc @@ -15,6 +15,8 @@ #include "BKE_animsys.h" +#include "RNA_path.h" + namespace blender::deg { /* Animated property storage. */ diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc index 4cbb2ce7060..be087c0b2d4 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc @@ -93,6 +93,7 @@ #include "BKE_world.h" #include "RNA_access.h" +#include "RNA_path.h" #include "RNA_prototypes.h" #include "RNA_types.h" diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.h b/source/blender/depsgraph/intern/builder/deg_builder_relations.h index 0cb0b60dfb0..7a78280f1f0 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.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" diff --git a/source/blender/depsgraph/intern/depsgraph_query.cc b/source/blender/depsgraph/intern/depsgraph_query.cc index 9a047c70d01..19339fa34ea 100644 --- a/source/blender/depsgraph/intern/depsgraph_query.cc +++ b/source/blender/depsgraph/intern/depsgraph_query.cc @@ -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" diff --git a/source/blender/draw/intern/draw_instance_data.c b/source/blender/draw/intern/draw_instance_data.c index 0e4e67f3320..88cc71fa0dd 100644 --- a/source/blender/draw/intern/draw_instance_data.c +++ b/source/blender/draw/intern/draw_instance_data.c @@ -27,6 +27,7 @@ #include "BKE_duplilist.h" #include "RNA_access.h" +#include "RNA_path.h" #include "BLI_bitmap.h" #include "BLI_memblock.h" diff --git a/source/blender/editors/animation/anim_channels_defines.c b/source/blender/editors/animation/anim_channels_defines.c index 729e8533d50..f665ec27b07 100644 --- a/source/blender/editors/animation/anim_channels_defines.c +++ b/source/blender/editors/animation/anim_channels_defines.c @@ -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" diff --git a/source/blender/editors/animation/anim_deps.c b/source/blender/editors/animation/anim_deps.c index ff53ad42e84..22c14983569 100644 --- a/source/blender/editors/animation/anim_deps.c +++ b/source/blender/editors/animation/anim_deps.c @@ -32,6 +32,7 @@ #include "DEG_depsgraph.h" #include "RNA_access.h" +#include "RNA_path.h" #include "SEQ_sequencer.h" #include "SEQ_utils.h" diff --git a/source/blender/editors/animation/anim_draw.c b/source/blender/editors/animation/anim_draw.c index d9dcbf1d57e..e352b4e26fe 100644 --- a/source/blender/editors/animation/anim_draw.c +++ b/source/blender/editors/animation/anim_draw.c @@ -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" diff --git a/source/blender/editors/animation/anim_ipo_utils.c b/source/blender/editors/animation/anim_ipo_utils.c index f01b3522547..93d83ff5f8e 100644 --- a/source/blender/editors/animation/anim_ipo_utils.c +++ b/source/blender/editors/animation/anim_ipo_utils.c @@ -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" diff --git a/source/blender/editors/animation/drivers.c b/source/blender/editors/animation/drivers.c index effedd4307d..63794caf5a7 100644 --- a/source/blender/editors/animation/drivers.c +++ b/source/blender/editors/animation/drivers.c @@ -39,6 +39,7 @@ #include "RNA_access.h" #include "RNA_define.h" +#include "RNA_path.h" #include "RNA_prototypes.h" #include "anim_intern.h" diff --git a/source/blender/editors/animation/keyframes_general.c b/source/blender/editors/animation/keyframes_general.c index 7723c221a40..40f0ac59b01 100644 --- a/source/blender/editors/animation/keyframes_general.c +++ b/source/blender/editors/animation/keyframes_general.c @@ -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" diff --git a/source/blender/editors/animation/keyframing.c b/source/blender/editors/animation/keyframing.c index 9084b9bb214..12f83343299 100644 --- a/source/blender/editors/animation/keyframing.c +++ b/source/blender/editors/animation/keyframing.c @@ -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" diff --git a/source/blender/editors/animation/keyingsets.c b/source/blender/editors/animation/keyingsets.c index 97b81277008..967a324ef95 100644 --- a/source/blender/editors/animation/keyingsets.c +++ b/source/blender/editors/animation/keyingsets.c @@ -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" diff --git a/source/blender/editors/armature/pose_slide.c b/source/blender/editors/armature/pose_slide.c index 38c99c2ef60..3e36a0d233a 100644 --- a/source/blender/editors/armature/pose_slide.c +++ b/source/blender/editors/armature/pose_slide.c @@ -53,6 +53,7 @@ #include "RNA_access.h" #include "RNA_define.h" +#include "RNA_path.h" #include "RNA_prototypes.h" #include "WM_api.h" diff --git a/source/blender/editors/armature/pose_utils.c b/source/blender/editors/armature/pose_utils.c index 032e0ec077c..ea038362532 100644 --- a/source/blender/editors/armature/pose_utils.c +++ b/source/blender/editors/armature/pose_utils.c @@ -26,6 +26,7 @@ #include "DEG_depsgraph.h" #include "RNA_access.h" +#include "RNA_path.h" #include "RNA_prototypes.h" #include "WM_api.h" diff --git a/source/blender/editors/interface/eyedroppers/eyedropper_driver.c b/source/blender/editors/interface/eyedroppers/eyedropper_driver.c index a9314df44a5..0f3062c3f61 100644 --- a/source/blender/editors/interface/eyedroppers/eyedropper_driver.c +++ b/source/blender/editors/interface/eyedroppers/eyedropper_driver.c @@ -24,6 +24,7 @@ #include "RNA_access.h" #include "RNA_define.h" +#include "RNA_path.h" #include "UI_interface.h" diff --git a/source/blender/editors/interface/interface_anim.c b/source/blender/editors/interface/interface_anim.c index 0e69b4bb358..d7d1d3ce260 100644 --- a/source/blender/editors/interface/interface_anim.c +++ b/source/blender/editors/interface/interface_anim.c @@ -35,6 +35,7 @@ #include "UI_interface.h" #include "RNA_access.h" +#include "RNA_path.h" #include "WM_api.h" #include "WM_types.h" diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c index ddd805f6010..4a5919864c7 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.c @@ -40,6 +40,7 @@ #include "RNA_access.h" #include "RNA_define.h" +#include "RNA_path.h" #include "RNA_prototypes.h" #include "RNA_types.h" diff --git a/source/blender/editors/interface/interface_region_menu_pie.cc b/source/blender/editors/interface/interface_region_menu_pie.cc index 09902dd6c35..8572e938b30 100644 --- a/source/blender/editors/interface/interface_region_menu_pie.cc +++ b/source/blender/editors/interface/interface_region_menu_pie.cc @@ -27,6 +27,7 @@ #include "WM_types.h" #include "RNA_access.h" +#include "RNA_path.h" #include "RNA_prototypes.h" #include "UI_interface.h" diff --git a/source/blender/editors/interface/interface_region_tooltip.c b/source/blender/editors/interface/interface_region_tooltip.c index 88fe866f717..f460a159a5f 100644 --- a/source/blender/editors/interface/interface_region_tooltip.c +++ b/source/blender/editors/interface/interface_region_tooltip.c @@ -39,6 +39,7 @@ #include "WM_types.h" #include "RNA_access.h" +#include "RNA_path.h" #include "UI_interface.h" diff --git a/source/blender/editors/object/object_constraint.c b/source/blender/editors/object/object_constraint.c index bf3b71178e8..abf286afa0c 100644 --- a/source/blender/editors/object/object_constraint.c +++ b/source/blender/editors/object/object_constraint.c @@ -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" diff --git a/source/blender/editors/screen/screen_user_menu.c b/source/blender/editors/screen/screen_user_menu.c index 9d66debda6f..01c208bf48d 100644 --- a/source/blender/editors/screen/screen_user_menu.c +++ b/source/blender/editors/screen/screen_user_menu.c @@ -34,6 +34,7 @@ #include "UI_resources.h" #include "RNA_access.h" +#include "RNA_path.h" #include "RNA_prototypes.h" /* -------------------------------------------------------------------- */ diff --git a/source/blender/editors/space_console/space_console.c b/source/blender/editors/space_console/space_console.c index c69b73e377d..90b3cec437c 100644 --- a/source/blender/editors/space_console/space_console.c +++ b/source/blender/editors/space_console/space_console.c @@ -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" diff --git a/source/blender/editors/space_graph/graph_buttons.c b/source/blender/editors/space_graph/graph_buttons.c index 3b8c6cbd1d0..d41904d9790 100644 --- a/source/blender/editors/space_graph/graph_buttons.c +++ b/source/blender/editors/space_graph/graph_buttons.c @@ -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" diff --git a/source/blender/editors/space_node/node_group.cc b/source/blender/editors/space_node/node_group.cc index d69d326c481..bb520c0537e 100644 --- a/source/blender/editors/space_node/node_group.cc +++ b/source/blender/editors/space_node/node_group.cc @@ -36,6 +36,7 @@ #include "RNA_access.h" #include "RNA_define.h" +#include "RNA_path.h" #include "RNA_prototypes.h" #include "WM_api.h" diff --git a/source/blender/editors/space_outliner/outliner_edit.cc b/source/blender/editors/space_outliner/outliner_edit.cc index 32860bc2cff..16da4f7b1dd 100644 --- a/source/blender/editors/space_outliner/outliner_edit.cc +++ b/source/blender/editors/space_outliner/outliner_edit.cc @@ -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" diff --git a/source/blender/editors/space_text/space_text.c b/source/blender/editors/space_text/space_text.c index ea35a8c0fa7..1b4acda9bcf 100644 --- a/source/blender/editors/space_text/space_text.c +++ b/source/blender/editors/space_text/space_text.c @@ -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 */ diff --git a/source/blender/io/collada/BCAnimationCurve.cpp b/source/blender/io/collada/BCAnimationCurve.cpp index 04a7a81c0a6..fe90dc5d5fa 100644 --- a/source/blender/io/collada/BCAnimationCurve.cpp +++ b/source/blender/io/collada/BCAnimationCurve.cpp @@ -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() diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h index 005228bea72..de9fa60aa5d 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -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 diff --git a/source/blender/makesrna/RNA_path.h b/source/blender/makesrna/RNA_path.h new file mode 100644 index 00000000000..7ab8c6fa313 --- /dev/null +++ b/source/blender/makesrna/RNA_path.h @@ -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 diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt index aaadd36341f..8124804de2b 100644 --- a/source/blender/makesrna/intern/CMakeLists.txt +++ b/source/blender/makesrna/intern/CMakeLists.txt @@ -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} diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index cf4182243b9..04707c01d6b 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -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) diff --git a/source/blender/makesrna/intern/rna_access_compare_override.c b/source/blender/makesrna/intern/rna_access_compare_override.c index 17c00923efa..d1df54df3cd 100644 --- a/source/blender/makesrna/intern/rna_access_compare_override.c +++ b/source/blender/makesrna/intern/rna_access_compare_override.c @@ -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" diff --git a/source/blender/makesrna/intern/rna_access_internal.h b/source/blender/makesrna/intern/rna_access_internal.h index a7dd35af670..384ff417fc9 100644 --- a/source/blender/makesrna/intern/rna_access_internal.h +++ b/source/blender/makesrna/intern/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 diff --git a/source/blender/makesrna/intern/rna_color.c b/source/blender/makesrna/intern/rna_color.c index 92cdcc6d781..2a85da42483 100644 --- a/source/blender/makesrna/intern/rna_color.c +++ b/source/blender/makesrna/intern/rna_color.c @@ -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" diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h index 833060e40f8..370455302b6 100644 --- a/source/blender/makesrna/intern/rna_internal.h +++ b/source/blender/makesrna/intern/rna_internal.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 diff --git a/source/blender/makesrna/intern/rna_path.cc b/source/blender/makesrna/intern/rna_path.cc new file mode 100644 index 00000000000..3e0fc9add80 --- /dev/null +++ b/source/blender/makesrna/intern/rna_path.cc @@ -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; +} diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index 14be9ceda94..d9c004fb6fa 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -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" diff --git a/source/blender/python/intern/bpy_rna_anim.c b/source/blender/python/intern/bpy_rna_anim.c index f25e9d0bbbc..d4a164d9482 100644 --- a/source/blender/python/intern/bpy_rna_anim.c +++ b/source/blender/python/intern/bpy_rna_anim.c @@ -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" diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index 315e4c994ad..1dc2307ba14 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -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" diff --git a/source/blender/windowmanager/message_bus/intern/wm_message_bus_rna.c b/source/blender/windowmanager/message_bus/intern/wm_message_bus_rna.c index be153195cee..5c745c32d2c 100644 --- a/source/blender/windowmanager/message_bus/intern/wm_message_bus_rna.c +++ b/source/blender/windowmanager/message_bus/intern/wm_message_bus_rna.c @@ -20,6 +20,7 @@ #include "message_bus/intern/wm_message_bus_intern.h" #include "RNA_access.h" +#include "RNA_path.h" /* -------------------------------------------------------------------- */ /** \name Internal Utilities |