Fix T41062: "copy to selected" doens't work for all attributes.
The issue was that some properties are no direct children of the struct we support in 'copy to selected' (RNA_Sequence in this case). Since we can't use the ID of sequences here (it's the scene, while we need a sequence level of control), we had to add a new API helper to RNA path, which takes a RNA type and return a path relative to the closest ancester of that type. This way, we get a path from the RNA_Sequence, and can easily apply it to all other valid sequences to copy the property. Review, suggestions and edits by Campbell Barton, thanks!
This commit is contained in:
parent
b37b317173
commit
5336e68e11
Notes:
blender-bot
2023-02-14 10:21:10 +01:00
Referenced by issue #41062, "copy to selected" doens't work for all attributes
|
@ -262,28 +262,43 @@ static void UI_OT_unset_property_button(wmOperatorType *ot)
|
|||
|
||||
/* Copy To Selected Operator ------------------------ */
|
||||
|
||||
static bool copy_to_selected_list(bContext *C, PointerRNA *ptr, ListBase *lb, bool *use_path)
|
||||
static bool copy_to_selected_list(
|
||||
bContext *C, PointerRNA *ptr, PropertyRNA *prop,
|
||||
ListBase *r_lb, bool *r_use_path_from_id, char **r_path)
|
||||
{
|
||||
*use_path = false;
|
||||
*r_use_path_from_id = false;
|
||||
*r_path = NULL;
|
||||
|
||||
if (RNA_struct_is_a(ptr->type, &RNA_EditBone))
|
||||
*lb = CTX_data_collection_get(C, "selected_editable_bones");
|
||||
else if (RNA_struct_is_a(ptr->type, &RNA_PoseBone))
|
||||
*lb = CTX_data_collection_get(C, "selected_pose_bones");
|
||||
else if (RNA_struct_is_a(ptr->type, &RNA_Sequence))
|
||||
*lb = CTX_data_collection_get(C, "selected_editable_sequences");
|
||||
else {
|
||||
if (RNA_struct_is_a(ptr->type, &RNA_EditBone)) {
|
||||
*r_lb = CTX_data_collection_get(C, "selected_editable_bones");
|
||||
}
|
||||
else if (RNA_struct_is_a(ptr->type, &RNA_PoseBone)) {
|
||||
*r_lb = CTX_data_collection_get(C, "selected_pose_bones");
|
||||
}
|
||||
else if (RNA_struct_is_a(ptr->type, &RNA_Sequence)) {
|
||||
*r_lb = CTX_data_collection_get(C, "selected_editable_sequences");
|
||||
}
|
||||
else if (ptr->id.data) {
|
||||
ID *id = ptr->id.data;
|
||||
|
||||
if (id && GS(id->name) == ID_OB) {
|
||||
*lb = CTX_data_collection_get(C, "selected_editable_objects");
|
||||
*use_path = true;
|
||||
if (GS(id->name) == ID_OB) {
|
||||
*r_lb = CTX_data_collection_get(C, "selected_editable_objects");
|
||||
*r_use_path_from_id = true;
|
||||
*r_path = RNA_path_from_ID_to_property(ptr, prop);
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
else if (GS(id->name) == ID_SCE) {
|
||||
/* Sequencer's ID is scene :/ */
|
||||
/* Try to recursively find an RNA_Sequence ancestor, to handle situations like T41062... */
|
||||
if ((*r_path = RNA_path_resolve_from_type_to_property(ptr, prop, &RNA_Sequence)) != NULL) {
|
||||
*r_lb = CTX_data_collection_get(C, "selected_editable_sequences");
|
||||
}
|
||||
}
|
||||
return (*r_path != NULL);
|
||||
}
|
||||
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -307,47 +322,54 @@ static bool copy_to_selected_button(bContext *C, bool all, bool poll)
|
|||
/* if there is a valid property that is editable... */
|
||||
if (ptr.data && prop) {
|
||||
char *path = NULL;
|
||||
bool use_path;
|
||||
bool use_path_from_id;
|
||||
CollectionPointerLink *link;
|
||||
ListBase lb;
|
||||
|
||||
if (!copy_to_selected_list(C, &ptr, &lb, &use_path))
|
||||
if (!copy_to_selected_list(C, &ptr, prop, &lb, &use_path_from_id, &path))
|
||||
return success;
|
||||
|
||||
if (!use_path || (path = RNA_path_from_ID_to_property(&ptr, prop))) {
|
||||
for (link = lb.first; link; link = link->next) {
|
||||
if (link->ptr.data != ptr.data) {
|
||||
if (use_path) {
|
||||
lprop = NULL;
|
||||
RNA_id_pointer_create(link->ptr.id.data, &idptr);
|
||||
RNA_path_resolve_property(&idptr, path, &lptr, &lprop);
|
||||
}
|
||||
else {
|
||||
lptr = link->ptr;
|
||||
lprop = prop;
|
||||
}
|
||||
for (link = lb.first; link; link = link->next) {
|
||||
if (link->ptr.data != ptr.data) {
|
||||
if (use_path_from_id) {
|
||||
/* Path relative to ID. */
|
||||
lprop = NULL;
|
||||
RNA_id_pointer_create(link->ptr.id.data, &idptr);
|
||||
RNA_path_resolve_property(&idptr, path, &lptr, &lprop);
|
||||
}
|
||||
else if (path) {
|
||||
/* Path relative to elements from list. */
|
||||
lprop = NULL;
|
||||
RNA_path_resolve_property(&link->ptr, path, &lptr, &lprop);
|
||||
}
|
||||
else {
|
||||
lptr = link->ptr;
|
||||
lprop = prop;
|
||||
}
|
||||
|
||||
if (lprop == prop) {
|
||||
if (RNA_property_editable(&lptr, lprop)) {
|
||||
if (poll) {
|
||||
if (lptr.data == ptr.data) {
|
||||
/* lptr might not be the same as link->ptr! */
|
||||
continue;
|
||||
}
|
||||
|
||||
if (lprop == prop) {
|
||||
if (RNA_property_editable(&lptr, lprop)) {
|
||||
if (poll) {
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
if (RNA_property_copy(&lptr, &ptr, prop, (all) ? -1 : index)) {
|
||||
RNA_property_update(C, &lptr, prop);
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
if (RNA_property_copy(&lptr, &ptr, prop, (all) ? -1 : index)) {
|
||||
RNA_property_update(C, &lptr, prop);
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (path)
|
||||
MEM_freeN(path);
|
||||
}
|
||||
|
||||
MEM_SAFE_FREE(path);
|
||||
BLI_freelistN(&lb);
|
||||
}
|
||||
|
||||
|
|
|
@ -927,9 +927,22 @@ bool RNA_path_resolve_property(PointerRNA *ptr, const char *path,
|
|||
bool RNA_path_resolve_property_full(PointerRNA *ptr, const char *path,
|
||||
PointerRNA *r_ptr, PropertyRNA **r_prop, int *r_index);
|
||||
|
||||
typedef struct PropertyElemRNA PropertyElemRNA;
|
||||
struct PropertyElemRNA {
|
||||
PropertyElemRNA *next, *prev;
|
||||
PointerRNA ptr;
|
||||
PropertyRNA *prop;
|
||||
int index;
|
||||
};
|
||||
bool RNA_path_resolve_elements(PointerRNA *ptr, const char *path, struct ListBase *r_elements);
|
||||
|
||||
char *RNA_path_from_ID_to_struct(PointerRNA *ptr);
|
||||
char *RNA_path_from_ID_to_property(PointerRNA *ptr, PropertyRNA *prop);
|
||||
|
||||
char *RNA_path_resolve_from_type_to_property(
|
||||
struct PointerRNA *ptr, struct PropertyRNA *prop,
|
||||
const struct StructRNA *type);
|
||||
|
||||
char *RNA_path_full_ID_py(struct ID *id);
|
||||
char *RNA_path_full_struct_py(struct PointerRNA *ptr);
|
||||
char *RNA_path_full_property_py(struct PointerRNA *ptr, struct PropertyRNA *prop, int index);
|
||||
|
|
|
@ -4015,11 +4015,14 @@ static bool rna_path_parse_array_index(const char **path, PointerRNA *ptr, Prope
|
|||
}
|
||||
|
||||
static bool rna_path_parse(PointerRNA *ptr, const char *path,
|
||||
PointerRNA *r_ptr, PropertyRNA **r_prop, int *index,
|
||||
PointerRNA *r_ptr, PropertyRNA **r_prop, int *r_index,
|
||||
ListBase *r_elements,
|
||||
const bool eval_pointer)
|
||||
{
|
||||
PropertyRNA *prop;
|
||||
PointerRNA curptr;
|
||||
PropertyElemRNA *prop_elem = NULL;
|
||||
int index = -1;
|
||||
char fixedbuf[256];
|
||||
int type;
|
||||
|
||||
|
@ -4061,6 +4064,14 @@ static bool rna_path_parse(PointerRNA *ptr, const char *path,
|
|||
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
|
||||
|
@ -4076,7 +4087,7 @@ static bool rna_path_parse(PointerRNA *ptr, const char *path,
|
|||
|
||||
curptr = nextptr;
|
||||
prop = NULL; /* now we have a PointerRNA, the prop is our parent so forget it */
|
||||
if (index) *index = -1;
|
||||
index = -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -4093,21 +4104,38 @@ static bool rna_path_parse(PointerRNA *ptr, const char *path,
|
|||
|
||||
curptr = nextptr;
|
||||
prop = NULL; /* now we have a PointerRNA, the prop is our parent so forget it */
|
||||
if (index) *index = -1;
|
||||
index = -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
if (index) {
|
||||
if (!rna_path_parse_array_index(&path, &curptr, prop, index))
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
*r_ptr = curptr;
|
||||
*r_prop = prop;
|
||||
if (r_ptr)
|
||||
*r_ptr = curptr;
|
||||
if (r_prop)
|
||||
*r_prop = prop;
|
||||
if (r_index)
|
||||
*r_index = index;
|
||||
|
||||
if (prop_elem && (prop_elem->ptr.data != curptr.data || prop_elem->prop != prop || prop_elem->index != index)) {
|
||||
PropertyElemRNA *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;
|
||||
}
|
||||
|
@ -4120,7 +4148,7 @@ static bool rna_path_parse(PointerRNA *ptr, const char *path,
|
|||
*/
|
||||
bool RNA_path_resolve(PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop)
|
||||
{
|
||||
if (!rna_path_parse(ptr, path, r_ptr, r_prop, NULL, true))
|
||||
if (!rna_path_parse(ptr, path, r_ptr, r_prop, NULL, NULL, true))
|
||||
return false;
|
||||
|
||||
return r_ptr->data != NULL;
|
||||
|
@ -4134,7 +4162,7 @@ bool RNA_path_resolve(PointerRNA *ptr, const char *path, PointerRNA *r_ptr, Prop
|
|||
*/
|
||||
bool RNA_path_resolve_full(PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop, int *r_index)
|
||||
{
|
||||
if (!rna_path_parse(ptr, path, r_ptr, r_prop, r_index, true))
|
||||
if (!rna_path_parse(ptr, path, r_ptr, r_prop, r_index, NULL, true))
|
||||
return false;
|
||||
|
||||
return r_ptr->data != NULL;
|
||||
|
@ -4149,7 +4177,7 @@ bool RNA_path_resolve_full(PointerRNA *ptr, const char *path, PointerRNA *r_ptr,
|
|||
*/
|
||||
bool RNA_path_resolve_property(PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop)
|
||||
{
|
||||
if (!rna_path_parse(ptr, path, r_ptr, r_prop, NULL, false))
|
||||
if (!rna_path_parse(ptr, path, r_ptr, r_prop, NULL, NULL, false))
|
||||
return false;
|
||||
|
||||
return r_ptr->data != NULL && *r_prop != NULL;
|
||||
|
@ -4165,12 +4193,25 @@ bool RNA_path_resolve_property(PointerRNA *ptr, const char *path, PointerRNA *r_
|
|||
*/
|
||||
bool RNA_path_resolve_property_full(PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop, int *r_index)
|
||||
{
|
||||
if (!rna_path_parse(ptr, path, r_ptr, r_prop, r_index, false))
|
||||
if (!rna_path_parse(ptr, path, r_ptr, r_prop, r_index, NULL, false))
|
||||
return false;
|
||||
|
||||
return r_ptr->data != NULL && *r_prop != NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the given RNA Path into a linked list of PropertyElemRNA's.
|
||||
*
|
||||
* To be used when complex operations over path are needed, like e.g. get relative paths, to avoid too much
|
||||
* string operations.
|
||||
*
|
||||
* \return True if there was no error while resolving the path
|
||||
* \note Assumes all pointers provided are valid
|
||||
*/
|
||||
bool RNA_path_resolve_elements(PointerRNA *ptr, const char *path, ListBase *r_elements)
|
||||
{
|
||||
return rna_path_parse(ptr, path, NULL, NULL, NULL, r_elements, false);
|
||||
}
|
||||
|
||||
char *RNA_path_append(const char *path, PointerRNA *UNUSED(ptr), PropertyRNA *prop, int intkey, const char *strkey)
|
||||
{
|
||||
|
@ -4497,6 +4538,47 @@ char *RNA_path_from_ID_to_property(PointerRNA *ptr, PropertyRNA *prop)
|
|||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* \return the path to given ptr/prop from the closest ancestor of given type, if any (else return NULL).
|
||||
*/
|
||||
char *RNA_path_resolve_from_type_to_property(
|
||||
PointerRNA *ptr, PropertyRNA *prop,
|
||||
const StructRNA *type)
|
||||
{
|
||||
/* Try to recursively find an "type"'d ancestor,
|
||||
* to handle situations where path from ID is not enough. */
|
||||
PointerRNA idptr;
|
||||
ListBase path_elems = {NULL};
|
||||
char *path = NULL;
|
||||
char *full_path = RNA_path_from_ID_to_property(ptr, prop);
|
||||
|
||||
if (full_path == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
RNA_id_pointer_create(ptr->id.data, &idptr);
|
||||
|
||||
if (RNA_path_resolve_elements(&idptr, full_path, &path_elems)) {
|
||||
PropertyElemRNA *prop_elem;
|
||||
|
||||
for (prop_elem = path_elems.last; prop_elem; prop_elem = prop_elem->prev) {
|
||||
if (RNA_struct_is_a(prop_elem->ptr.type, type)) {
|
||||
char *ref_path = RNA_path_from_ID_to_struct(&prop_elem->ptr);
|
||||
if (ref_path) {
|
||||
path = BLI_strdup(full_path + strlen(ref_path) + 1); /* +1 for the linking '.' */
|
||||
MEM_freeN(ref_path);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
BLI_freelistN(&path_elems);
|
||||
}
|
||||
|
||||
MEM_freeN(full_path);
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the ID as a python representation, eg:
|
||||
* bpy.data.foo["bar"]
|
||||
|
|
Loading…
Reference in New Issue