Changeset View
Changeset View
Standalone View
Standalone View
source/blender/makesrna/intern/rna_access.c
| Show All 40 Lines | |||||
| #include "BKE_report.h" | #include "BKE_report.h" | ||||
| #include "DEG_depsgraph.h" | #include "DEG_depsgraph.h" | ||||
| #include "DEG_depsgraph_build.h" | #include "DEG_depsgraph_build.h" | ||||
| #include "RNA_access.h" | #include "RNA_access.h" | ||||
| #include "RNA_define.h" | #include "RNA_define.h" | ||||
| #include "RNA_enum_types.h" | #include "RNA_enum_types.h" | ||||
| #include "RNA_path.h" | |||||
| #include "WM_api.h" | #include "WM_api.h" | ||||
| #include "WM_message.h" | #include "WM_message.h" | ||||
| /* flush updates */ | /* flush updates */ | ||||
| #include "DNA_object_types.h" | #include "DNA_object_types.h" | ||||
| #include "WM_types.h" | #include "WM_types.h" | ||||
| ▲ Show 20 Lines • Show All 676 Lines • ▼ Show 20 Lines | if (RNA_property_collection_lookup_string(ptr, iterprop, identifier, &propptr)) { | ||||
| return propptr.data; | return propptr.data; | ||||
| } | } | ||||
| } | } | ||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| /* Find the property which uses the given nested struct */ | /* 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; | PropertyRNA *prop = NULL; | ||||
| RNA_STRUCT_BEGIN (ptr, iprop) { | RNA_STRUCT_BEGIN (ptr, iprop) { | ||||
| /* This assumes that there can only be one user of this nested struct */ | /* This assumes that there can only be one user of this nested struct */ | ||||
| if (RNA_property_pointer_type(ptr, iprop) == srna) { | if (RNA_property_pointer_type(ptr, iprop) == srna) { | ||||
| prop = iprop; | prop = iprop; | ||||
| break; | break; | ||||
| ▲ Show 20 Lines • Show All 667 Lines • ▼ Show 20 Lines | StructRNA *RNA_property_pointer_type(PointerRNA *ptr, PropertyRNA *prop) | ||||
| } | } | ||||
| else if (prop->type == PROP_COLLECTION) { | else if (prop->type == PROP_COLLECTION) { | ||||
| CollectionPropertyRNA *cprop = (CollectionPropertyRNA *)prop; | CollectionPropertyRNA *cprop = (CollectionPropertyRNA *)prop; | ||||
| if (cprop->item_type) { | if (cprop->item_type) { | ||||
| return cprop->item_type; | 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; | return &RNA_UnknownType; | ||||
| } | } | ||||
| bool RNA_property_pointer_poll(PointerRNA *ptr, PropertyRNA *prop, PointerRNA *value) | bool RNA_property_pointer_poll(PointerRNA *ptr, PropertyRNA *prop, PointerRNA *value) | ||||
| { | { | ||||
| prop = rna_ensure_property(prop); | prop = rna_ensure_property(prop); | ||||
| ▲ Show 20 Lines • Show All 3,382 Lines • ▼ Show 20 Lines | |||||
| { | { | ||||
| if (index < 0 || index >= length) { | if (index < 0 || index >= length) { | ||||
| return PointerRNA_NULL; | return PointerRNA_NULL; | ||||
| } | } | ||||
| return rna_pointer_inherit_refine(ptr, type, ((char *)data) + index * itemsize); | 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 */ | /* Quick name based property access */ | ||||
| bool RNA_boolean_get(PointerRNA *ptr, const char *name) | bool RNA_boolean_get(PointerRNA *ptr, const char *name) | ||||
| { | { | ||||
| PropertyRNA *prop = RNA_struct_find_property(ptr, name); | PropertyRNA *prop = RNA_struct_find_property(ptr, name); | ||||
| if (prop) { | if (prop) { | ||||
| return RNA_property_boolean_get(ptr, prop); | return RNA_property_boolean_get(ptr, prop); | ||||
| ▲ Show 20 Lines • Show All 1,954 Lines • Show Last 20 Lines | |||||