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:
Bastien Montagne 2014-08-05 18:59:02 +02:00
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
3 changed files with 170 additions and 53 deletions

View File

@ -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);
}

View File

@ -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);

View File

@ -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"]