"copy to selected" doens't work for all attributes #41062

Closed
opened 2014-07-13 22:02:39 +02:00 by Bob · 18 comments

System Information
Windows 7

Blender Version
Blender 2.71

In video sequencer there are a few attributes that inexplicably have the option 'copy to selected' greyed out,even though they look like it should work. I don't know for sure if this ever worked,or if it's broken by design.
The attributes in question are 'image offset" and "image crop".

Also modifiers can't be copied,though this might not be a bug. But maybe it should be considered as well,as it would be a useful addition.

Copy to selected is an important function to make working in the sequencer less of a hassle ,and thus should work on every attribute that is not a global setting.

Tahnk you for looking into this.

Kind regards

**System Information** Windows 7 **Blender Version** Blender 2.71 In video sequencer there are a few attributes that inexplicably have the option 'copy to selected' greyed out,even though they look like it should work. I don't know for sure if this ever worked,or if it's broken by design. The attributes in question are 'image offset" and "image crop". Also modifiers can't be copied,though this might not be a bug. But maybe it should be considered as well,as it would be a useful addition. Copy to selected is an important function to make working in the sequencer less of a hassle ,and thus should work on every attribute that is not a global setting. Tahnk you for looking into this. Kind regards
Author

Changed status to: 'Open'

Changed status to: 'Open'
Author

Added subscriber: @Bollebib

Added subscriber: @Bollebib
Author

extra info

The toggles of image crop and image offset DO work,but not their numerical settings. Maybe because having sub-options is the reason why this does not work.

extra info The toggles of image crop and image offset DO work,but not their numerical settings. Maybe because having sub-options is the reason why this does not work.

Added subscriber: @mont29

Added subscriber: @mont29

Added subscriber: @Sergey

Added subscriber: @Sergey

For me Image Offset and Image Crop works just fine. Please provide .blend which demonstrates the issue.

As for modifiers copy, it's just a missing feature, not considered a bug.

For me Image Offset and Image Crop works just fine. Please provide .blend which demonstrates the issue. As for modifiers copy, it's just a missing feature, not considered a bug.
Author

Savetoselected.blend

added the blend.

I do want to repeat: the TOGGLES of the offset and crop are fine,those do get copied,but their values ( X and Y,top ,left,bottom,right ) are not.

For the modifiers ,I kind of expected that,but it maybe should be considered.

(do I have to change priority to 'needs triage" again btw? not sure what the guidelines are)

[Savetoselected.blend](https://archive.blender.org/developer/F97785/Savetoselected.blend) added the blend. I do want to repeat: the TOGGLES of the offset and crop are fine,those do get copied,but their values ( X and Y,top ,left,bottom,right ) are not. For the modifiers ,I kind of expected that,but it maybe should be considered. (do I have to change priority to 'needs triage" again btw? not sure what the guidelines are)
Bastien Montagne self-assigned this 2014-07-14 15:55:08 +02:00

So, was a bit more difficult than expected, but here is a patch that enables that feature for those settings:

P88: #41062

diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c
index 877a993..6d8f849 100644
--- a/source/blender/editors/interface/interface_ops.c
+++ b/source/blender/editors/interface/interface_ops.c
@@ -258,9 +258,11 @@ 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 *lb,
+                                  bool *use_path_from_id, char **path)
 {
-	*use_path = false;
+	*use_path_from_id = false;
+	*path = NULL;
 
 	if (RNA_struct_is_a(ptr->type, &RNA_EditBone))
 		*lb = CTX_data_collection_get(C, "selected_editable_bones");
@@ -271,15 +273,44 @@ static bool copy_to_selected_list(bContext *C, PointerRNA *ptr, ListBase *lb, bo
 	else {
 		ID *id = ptr->id.data;
 
-		if (id && GS(id->name) == ID_OB) {
-			*lb = CTX_data_collection_get(C, "selected_editable_objects");
-			*use_path = true;
-		}
-		else {
-			return false;
+		if (id) {
+			if (GS(id->name) == ID_OB) {
+				*lb = CTX_data_collection_get(C, "selected_editable_objects");
+				*use_path_from_id = true;
+				*path = RNA_path_from_ID_to_property(ptr, prop);
+			}
+			else {
+				/* Try to recursively find an RNA_Sequence ancestor, to handle situations like #41062... */
+				PointerRNA idptr, tptr;
+				PropertyRNA *tprop;
+				char *full_path = RNA_path_from_ID_to_property(ptr, prop);
+				char *path_t1 = RNA_path_back(full_path);  /* We remove property... */
+				char *path_t2 = RNA_path_back(path_t1);  /* ... and first struct, which we already know as 'invalid'. */
+				MEM_SAFE_FREE(path_t1);
+				path_t1 = path_t2;
+
+				RNA_id_pointer_create(ptr->id.data, &idptr);
+
+				while (path_t1 && RNA_path_resolve(&idptr, path_t1, &tptr, &tprop)) {
+					if (RNA_struct_is_a(tptr.type, &RNA_Sequence)) {
+						*lb = CTX_data_collection_get(C, "selected_editable_sequences");
+						*path = BLI_strdup(full_path + strlen(path_t1) + 1);  /* +1 for the linking '.' */
+						MEM_SAFE_FREE(path_t1);
+						break;
+					}
+
+					path_t2 = RNA_path_back(path_t1);
+					MEM_SAFE_FREE(path_t1);
+					path_t1 = path_t2;
+				}
+
+				MEM_SAFE_FREE(full_path);
+			}
+			return (*path != NULL);
 		}
+		return false;
 	}
-	
+
 	return true;
 }
 
@@ -303,47 +334,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);
 	}
 

Main issue was, these properties are in a sub-RNA struct, not directly in RNA_Sequence one, and since sequences' ID is the scene itself, we could not use current 'from ID' fallback used for objects.

Now, we are looping back in structs’ hierarchy to try to find a RNA_Sequence one, and give a 'relative' path from those. Seems to work pretty well, just not sure about performances issues here…

Sergey, what do you think? Maybe ask Campbell's advice too?

So, was a bit more difficult than expected, but here is a patch that enables that feature for those settings: [P88: #41062](https://archive.blender.org/developer/P88.txt) ``` diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c index 877a993..6d8f849 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.c @@ -258,9 +258,11 @@ 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 *lb, + bool *use_path_from_id, char **path) { - *use_path = false; + *use_path_from_id = false; + *path = NULL; if (RNA_struct_is_a(ptr->type, &RNA_EditBone)) *lb = CTX_data_collection_get(C, "selected_editable_bones"); @@ -271,15 +273,44 @@ static bool copy_to_selected_list(bContext *C, PointerRNA *ptr, ListBase *lb, bo else { ID *id = ptr->id.data; - if (id && GS(id->name) == ID_OB) { - *lb = CTX_data_collection_get(C, "selected_editable_objects"); - *use_path = true; - } - else { - return false; + if (id) { + if (GS(id->name) == ID_OB) { + *lb = CTX_data_collection_get(C, "selected_editable_objects"); + *use_path_from_id = true; + *path = RNA_path_from_ID_to_property(ptr, prop); + } + else { + /* Try to recursively find an RNA_Sequence ancestor, to handle situations like #41062... */ + PointerRNA idptr, tptr; + PropertyRNA *tprop; + char *full_path = RNA_path_from_ID_to_property(ptr, prop); + char *path_t1 = RNA_path_back(full_path); /* We remove property... */ + char *path_t2 = RNA_path_back(path_t1); /* ... and first struct, which we already know as 'invalid'. */ + MEM_SAFE_FREE(path_t1); + path_t1 = path_t2; + + RNA_id_pointer_create(ptr->id.data, &idptr); + + while (path_t1 && RNA_path_resolve(&idptr, path_t1, &tptr, &tprop)) { + if (RNA_struct_is_a(tptr.type, &RNA_Sequence)) { + *lb = CTX_data_collection_get(C, "selected_editable_sequences"); + *path = BLI_strdup(full_path + strlen(path_t1) + 1); /* +1 for the linking '.' */ + MEM_SAFE_FREE(path_t1); + break; + } + + path_t2 = RNA_path_back(path_t1); + MEM_SAFE_FREE(path_t1); + path_t1 = path_t2; + } + + MEM_SAFE_FREE(full_path); + } + return (*path != NULL); } + return false; } - + return true; } @@ -303,47 +334,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); } ``` Main issue was, these properties are in a sub-RNA struct, not directly in RNA_Sequence one, and since sequences' ID is the scene itself, we could not use current 'from ID' fallback used for objects. Now, we are looping back in structs’ hierarchy to try to find a RNA_Sequence one, and give a 'relative' path from those. Seems to work pretty well, just not sure about performances issues here… Sergey, what do you think? Maybe ask Campbell's advice too?

Added subscriber: @ideasman42

Added subscriber: @ideasman42

Campbell, we need your advice on this one…

Campbell, we need your advice on this one…
Author

Great job, mont29

I hope this will get into the next release?

Or do you still need campbellbarton's feedback?

Great job, mont29 I hope this will get into the next release? Or do you still need campbellbarton's feedback?

@mont29

This patch makes code more complex in way thats not easy for me to reason about, for example - there is a recursive loop on properties, but I cant validate it works right with any sequencer options...

Are you sure a loop is even needed here? - the property resolving functions will get a path back to the ID in one step, so AFAICS loops not needed.


So general feedback...

Theres a large block of code under:

  if (GS(id->name) == ID_OB) {
  ...
  else {
      <---- HERE ---- >
  }

Thats only used for sequencer props. 2x suggestions...

  • This should only need to check for if (GS(id->name) == ID_SCE) {
  • The path finding part IMHO should be refactored out into its own function. Since its quite large and check for Sequence Strip is nested away.
@mont29 This patch makes code more complex in way thats not easy for me to reason about, for example - there is a recursive loop on properties, but I cant validate it works right with any sequencer options... Are you sure a loop is even needed here? - the property resolving functions will get a path back to the ID in one step, so AFAICS loops not needed. ---- So general feedback... Theres a large block of code under: ``` if (GS(id->name) == ID_OB) { ... else { <---- HERE ---- > } ``` Thats only used for sequencer props. 2x suggestions... - This should only need to check for `if (GS(id->name) == ID_SCE) {` - The path finding part IMHO should be refactored out into its own function. Since its quite large and check for Sequence Strip is nested away.

So updated patch:

  • Made recursing path code a new func in RNA API (not sure about its naming, though…), since this might be rather useful in other situations as well.
  • Yes, loop is needed, see e.g. sub-props (color etc.) of gain/gamma/lift of color balance modifier (two levels)…

P117: T41062_02.diff

diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c
index 32b073b..3855ce4 100644
--- a/source/blender/editors/interface/interface_ops.c
+++ b/source/blender/editors/interface/interface_ops.c
@@ -262,9 +262,11 @@ 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 *lb,
+                                  bool *use_path_from_id, char **path)
 {
-	*use_path = false;
+	*use_path_from_id = false;
+	*path = NULL;
 
 	if (RNA_struct_is_a(ptr->type, &RNA_EditBone))
 		*lb = CTX_data_collection_get(C, "selected_editable_bones");
@@ -275,15 +277,23 @@ static bool copy_to_selected_list(bContext *C, PointerRNA *ptr, ListBase *lb, bo
 	else {
 		ID *id = ptr->id.data;
 
-		if (id && GS(id->name) == ID_OB) {
-			*lb = CTX_data_collection_get(C, "selected_editable_objects");
-			*use_path = true;
-		}
-		else {
-			return false;
+		if (id) {
+			if (GS(id->name) == ID_OB) {
+				*lb = CTX_data_collection_get(C, "selected_editable_objects");
+				*use_path_from_id = true;
+				*path = RNA_path_from_ID_to_property(ptr, prop);
+			}
+			else if (GS(id->name) == ID_SCE) {  /* Sequencer's ID is scene :/ */
+				/* Try to recursively find an RNA_Sequence ancestor, to handle situations like #41062... */
+				if ((*path = RNA_path_from_ancestor_type_to_property(ptr, prop, &RNA_Sequence)) != NULL) {
+					*lb = CTX_data_collection_get(C, "selected_editable_sequences");
+				}
+			}
+			return (*path != NULL);
 		}
+		return false;
 	}
-	
+
 	return true;
 }
 
@@ -307,47 +317,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);
 	}
 
diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h
index a2bbaf6..f888734 100644
--- a/source/blender/makesrna/RNA_access.h
+++ b/source/blender/makesrna/RNA_access.h
@@ -930,6 +930,8 @@ bool RNA_path_resolve_property_full(PointerRNA *ptr, const char *path,
 char *RNA_path_from_ID_to_struct(PointerRNA *ptr);
 char *RNA_path_from_ID_to_property(PointerRNA *ptr, PropertyRNA *prop);
 
+char *RNA_path_from_ancestor_type_to_property(struct PointerRNA *ptr, struct PropertyRNA *prop, 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);
diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c
index 271f907..d98ef45 100644
--- a/source/blender/makesrna/intern/rna_access.c
+++ b/source/blender/makesrna/intern/rna_access.c
@@ -4498,6 +4498,40 @@ char *RNA_path_from_ID_to_property(PointerRNA *ptr, PropertyRNA *prop)
 }
 
 /**
+ * Assumes ptr is not an instance of type!
+ * \return the path to given ptr/prop from the closest ancestor of given type, if any (else return NULL).
+ */
+char *RNA_path_from_ancestor_type_to_property(PointerRNA *ptr, PropertyRNA *prop, StructRNA *type)
+{
+	/* Try to recursively find an "type"'d ancestor, to handle situations where path from ID is not enough. */
+	PointerRNA idptr, tptr;
+	PropertyRNA *tprop;
+	char *path = NULL;
+	char *full_path = RNA_path_from_ID_to_property(ptr, prop);
+	char *path_t1 = RNA_path_back(full_path);  /* We remove property... */
+	char *path_t2 = RNA_path_back(path_t1);  /* ... and first struct, which we assume as 'invalid'. */
+	MEM_SAFE_FREE(path_t1);
+	path_t1 = path_t2;
+
+	RNA_id_pointer_create(ptr->id.data, &idptr);
+
+	while (path_t1 && RNA_path_resolve(&idptr, path_t1, &tptr, &tprop)) {
+		if (RNA_struct_is_a(tptr.type, type)) {
+			path = BLI_strdup(full_path + strlen(path_t1) + 1);  /* +1 for the linking '.' */
+			MEM_SAFE_FREE(path_t1);
+			break;
+		}
+
+		path_t2 = RNA_path_back(path_t1);
+		MEM_SAFE_FREE(path_t1);
+		path_t1 = path_t2;
+	}
+
+	MEM_SAFE_FREE(full_path);
+	return path;
+}
+
+/**
  * Get the ID as a python representation, eg:
  *   bpy.data.foo["bar"]
  */

So updated patch: * Made recursing path code a new func in RNA API (not sure about its naming, though…), since this might be rather useful in other situations as well. * Yes, loop is needed, see e.g. sub-props (color etc.) of gain/gamma/lift of color balance modifier (two levels)… [P117: T41062_02.diff](https://archive.blender.org/developer/P117.txt) ``` diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c index 32b073b..3855ce4 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.c @@ -262,9 +262,11 @@ 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 *lb, + bool *use_path_from_id, char **path) { - *use_path = false; + *use_path_from_id = false; + *path = NULL; if (RNA_struct_is_a(ptr->type, &RNA_EditBone)) *lb = CTX_data_collection_get(C, "selected_editable_bones"); @@ -275,15 +277,23 @@ static bool copy_to_selected_list(bContext *C, PointerRNA *ptr, ListBase *lb, bo else { ID *id = ptr->id.data; - if (id && GS(id->name) == ID_OB) { - *lb = CTX_data_collection_get(C, "selected_editable_objects"); - *use_path = true; - } - else { - return false; + if (id) { + if (GS(id->name) == ID_OB) { + *lb = CTX_data_collection_get(C, "selected_editable_objects"); + *use_path_from_id = true; + *path = RNA_path_from_ID_to_property(ptr, prop); + } + else if (GS(id->name) == ID_SCE) { /* Sequencer's ID is scene :/ */ + /* Try to recursively find an RNA_Sequence ancestor, to handle situations like #41062... */ + if ((*path = RNA_path_from_ancestor_type_to_property(ptr, prop, &RNA_Sequence)) != NULL) { + *lb = CTX_data_collection_get(C, "selected_editable_sequences"); + } + } + return (*path != NULL); } + return false; } - + return true; } @@ -307,47 +317,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); } diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h index a2bbaf6..f888734 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -930,6 +930,8 @@ bool RNA_path_resolve_property_full(PointerRNA *ptr, const char *path, char *RNA_path_from_ID_to_struct(PointerRNA *ptr); char *RNA_path_from_ID_to_property(PointerRNA *ptr, PropertyRNA *prop); +char *RNA_path_from_ancestor_type_to_property(struct PointerRNA *ptr, struct PropertyRNA *prop, 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); diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index 271f907..d98ef45 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -4498,6 +4498,40 @@ char *RNA_path_from_ID_to_property(PointerRNA *ptr, PropertyRNA *prop) } /** + * Assumes ptr is not an instance of type! + * \return the path to given ptr/prop from the closest ancestor of given type, if any (else return NULL). + */ +char *RNA_path_from_ancestor_type_to_property(PointerRNA *ptr, PropertyRNA *prop, StructRNA *type) +{ + /* Try to recursively find an "type"'d ancestor, to handle situations where path from ID is not enough. */ + PointerRNA idptr, tptr; + PropertyRNA *tprop; + char *path = NULL; + char *full_path = RNA_path_from_ID_to_property(ptr, prop); + char *path_t1 = RNA_path_back(full_path); /* We remove property... */ + char *path_t2 = RNA_path_back(path_t1); /* ... and first struct, which we assume as 'invalid'. */ + MEM_SAFE_FREE(path_t1); + path_t1 = path_t2; + + RNA_id_pointer_create(ptr->id.data, &idptr); + + while (path_t1 && RNA_path_resolve(&idptr, path_t1, &tptr, &tprop)) { + if (RNA_struct_is_a(tptr.type, type)) { + path = BLI_strdup(full_path + strlen(path_t1) + 1); /* +1 for the linking '.' */ + MEM_SAFE_FREE(path_t1); + break; + } + + path_t2 = RNA_path_back(path_t1); + MEM_SAFE_FREE(path_t1); + path_t1 = path_t2; + } + + MEM_SAFE_FREE(full_path); + return path; +} + +/** * Get the ID as a python representation, eg: * bpy.data.foo["bar"] */ ```

Third attempt, from discussion with Campbell on IRC (try to avoid as much string manipulation as possible):

P118: T41062_03.diff

diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c
index 32b073b..3855ce4 100644
--- a/source/blender/editors/interface/interface_ops.c
+++ b/source/blender/editors/interface/interface_ops.c
@@ -262,9 +262,11 @@ 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 *lb,
+                                  bool *use_path_from_id, char **path)
 {
-	*use_path = false;
+	*use_path_from_id = false;
+	*path = NULL;
 
 	if (RNA_struct_is_a(ptr->type, &RNA_EditBone))
 		*lb = CTX_data_collection_get(C, "selected_editable_bones");
@@ -275,15 +277,23 @@ static bool copy_to_selected_list(bContext *C, PointerRNA *ptr, ListBase *lb, bo
 	else {
 		ID *id = ptr->id.data;
 
-		if (id && GS(id->name) == ID_OB) {
-			*lb = CTX_data_collection_get(C, "selected_editable_objects");
-			*use_path = true;
-		}
-		else {
-			return false;
+		if (id) {
+			if (GS(id->name) == ID_OB) {
+				*lb = CTX_data_collection_get(C, "selected_editable_objects");
+				*use_path_from_id = true;
+				*path = RNA_path_from_ID_to_property(ptr, prop);
+			}
+			else if (GS(id->name) == ID_SCE) {  /* Sequencer's ID is scene :/ */
+				/* Try to recursively find an RNA_Sequence ancestor, to handle situations like #41062... */
+				if ((*path = RNA_path_from_ancestor_type_to_property(ptr, prop, &RNA_Sequence)) != NULL) {
+					*lb = CTX_data_collection_get(C, "selected_editable_sequences");
+				}
+			}
+			return (*path != NULL);
 		}
+		return false;
 	}
-	
+
 	return true;
 }
 
@@ -307,47 +317,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);
 	}
 
diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h
index a2bbaf6..1b16b22 100644
--- a/source/blender/makesrna/RNA_access.h
+++ b/source/blender/makesrna/RNA_access.h
@@ -927,9 +927,20 @@ 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 RNA_PathElement RNA_PathElement;
+struct RNA_PathElement {
+	RNA_PathElement *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_from_ancestor_type_to_property(struct PointerRNA *ptr, struct PropertyRNA *prop, 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);
diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c
index 271f907..aa91337 100644
--- a/source/blender/makesrna/intern/rna_access.c
+++ b/source/blender/makesrna/intern/rna_access.c
@@ -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;
+	RNA_PathElement *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) {
+			elem = MEM_mallocN(sizeof(RNA_PathElement), __func__);
+			elem->ptr = curptr;
+			elem->prop = prop;
+			elem->index = -1;  /* index will be added later, if needed. */
+			BLI_addtail(r_elements, 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,33 @@ 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 || elem) {
+					if (!rna_path_parse_array_index(&path, &curptr, prop, &index))
 						return false;
+					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 (elem && (elem->ptr.data != curptr.data || elem->prop != prop || elem->index != index)) {
+		RNA_PathElement *elem = MEM_mallocN(sizeof(RNA_PathElement), __func__);
+		elem->ptr = curptr;
+		elem->prop = prop;
+		elem->index = index;
+		BLI_addtail(r_elements, elem);
+	}
 
 	return true;
 }
@@ -4120,7 +4143,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 +4157,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 +4172,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 +4188,24 @@ 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 RNA_PathElement's.
+ *
+ * To be used when complex operations over path are needed, like e.g. get relative paths, to avoid too much
+ * string operations.
+ * \note Assumes all pointers provided are valid
+ * \return True only if both a valid final pointer and property are found after resolving the path
+ */
+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)
 {
@@ -4498,6 +4533,39 @@ char *RNA_path_from_ID_to_property(PointerRNA *ptr, PropertyRNA *prop)
 }
 
 /**
+ * Assumes ptr is not an instance of type!
+ * \return the path to given ptr/prop from the closest ancestor of given type, if any (else return NULL).
+ */
+char *RNA_path_from_ancestor_type_to_property(PointerRNA *ptr, PropertyRNA *prop, StructRNA *type)
+{
+	/* Try to recursively find an "type"'d ancestor, to handle situations where path from ID is not enough. */
+	PointerRNA idptr;
+	ListBase path_elements = {NULL};
+	char *path = NULL;
+	char *full_path = RNA_path_from_ID_to_property(ptr, prop);
+
+	RNA_id_pointer_create(ptr->id.data, &idptr);
+
+	if (RNA_path_resolve_elements(&idptr, full_path, &path_elements)) {
+		RNA_PathElement *elem;
+
+		for (elem = path_elements.last; elem; elem = elem->prev) {
+			if (RNA_struct_is_a(elem->ptr.type, type)) {
+				char *ref_path = RNA_path_from_ID_to_struct(&elem->ptr);
+				path = BLI_strdup(full_path + strlen(ref_path) + 1);  /* +1 for the linking '.' */
+				MEM_freeN(ref_path);
+				break;
+			}
+		}
+
+		BLI_freelistN(&path_elements);
+	}
+
+	MEM_SAFE_FREE(full_path);
+	return path;
+}
+
+/**
  * Get the ID as a python representation, eg:
  *   bpy.data.foo["bar"]
  */

Third attempt, from discussion with Campbell on IRC (try to avoid as much string manipulation as possible): [P118: T41062_03.diff](https://archive.blender.org/developer/P118.txt) ``` diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c index 32b073b..3855ce4 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.c @@ -262,9 +262,11 @@ 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 *lb, + bool *use_path_from_id, char **path) { - *use_path = false; + *use_path_from_id = false; + *path = NULL; if (RNA_struct_is_a(ptr->type, &RNA_EditBone)) *lb = CTX_data_collection_get(C, "selected_editable_bones"); @@ -275,15 +277,23 @@ static bool copy_to_selected_list(bContext *C, PointerRNA *ptr, ListBase *lb, bo else { ID *id = ptr->id.data; - if (id && GS(id->name) == ID_OB) { - *lb = CTX_data_collection_get(C, "selected_editable_objects"); - *use_path = true; - } - else { - return false; + if (id) { + if (GS(id->name) == ID_OB) { + *lb = CTX_data_collection_get(C, "selected_editable_objects"); + *use_path_from_id = true; + *path = RNA_path_from_ID_to_property(ptr, prop); + } + else if (GS(id->name) == ID_SCE) { /* Sequencer's ID is scene :/ */ + /* Try to recursively find an RNA_Sequence ancestor, to handle situations like #41062... */ + if ((*path = RNA_path_from_ancestor_type_to_property(ptr, prop, &RNA_Sequence)) != NULL) { + *lb = CTX_data_collection_get(C, "selected_editable_sequences"); + } + } + return (*path != NULL); } + return false; } - + return true; } @@ -307,47 +317,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); } diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h index a2bbaf6..1b16b22 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -927,9 +927,20 @@ 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 RNA_PathElement RNA_PathElement; +struct RNA_PathElement { + RNA_PathElement *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_from_ancestor_type_to_property(struct PointerRNA *ptr, struct PropertyRNA *prop, 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); diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index 271f907..aa91337 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -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; + RNA_PathElement *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) { + elem = MEM_mallocN(sizeof(RNA_PathElement), __func__); + elem->ptr = curptr; + elem->prop = prop; + elem->index = -1; /* index will be added later, if needed. */ + BLI_addtail(r_elements, 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,33 @@ 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 || elem) { + if (!rna_path_parse_array_index(&path, &curptr, prop, &index)) return false; + 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 (elem && (elem->ptr.data != curptr.data || elem->prop != prop || elem->index != index)) { + RNA_PathElement *elem = MEM_mallocN(sizeof(RNA_PathElement), __func__); + elem->ptr = curptr; + elem->prop = prop; + elem->index = index; + BLI_addtail(r_elements, elem); + } return true; } @@ -4120,7 +4143,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 +4157,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 +4172,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 +4188,24 @@ 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 RNA_PathElement's. + * + * To be used when complex operations over path are needed, like e.g. get relative paths, to avoid too much + * string operations. + * \note Assumes all pointers provided are valid + * \return True only if both a valid final pointer and property are found after resolving the path + */ +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) { @@ -4498,6 +4533,39 @@ char *RNA_path_from_ID_to_property(PointerRNA *ptr, PropertyRNA *prop) } /** + * Assumes ptr is not an instance of type! + * \return the path to given ptr/prop from the closest ancestor of given type, if any (else return NULL). + */ +char *RNA_path_from_ancestor_type_to_property(PointerRNA *ptr, PropertyRNA *prop, StructRNA *type) +{ + /* Try to recursively find an "type"'d ancestor, to handle situations where path from ID is not enough. */ + PointerRNA idptr; + ListBase path_elements = {NULL}; + char *path = NULL; + char *full_path = RNA_path_from_ID_to_property(ptr, prop); + + RNA_id_pointer_create(ptr->id.data, &idptr); + + if (RNA_path_resolve_elements(&idptr, full_path, &path_elements)) { + RNA_PathElement *elem; + + for (elem = path_elements.last; elem; elem = elem->prev) { + if (RNA_struct_is_a(elem->ptr.type, type)) { + char *ref_path = RNA_path_from_ID_to_struct(&elem->ptr); + path = BLI_strdup(full_path + strlen(ref_path) + 1); /* +1 for the linking '.' */ + MEM_freeN(ref_path); + break; + } + } + + BLI_freelistN(&path_elements); + } + + MEM_SAFE_FREE(full_path); + return path; +} + +/** * Get the ID as a python representation, eg: * bpy.data.foo["bar"] */ ```

Updated P120: Fork of P118 T41062_04.diff

diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c
index 32b073b..458aca4 100644
--- a/source/blender/editors/interface/interface_ops.c
+++ b/source/blender/editors/interface/interface_ops.c
@@ -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 #41062... */
+			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);
 	}
 
diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h
index a2bbaf6..ba2dd8b 100644
--- a/source/blender/makesrna/RNA_access.h
+++ b/source/blender/makesrna/RNA_access.h
@@ -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);
diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c
index 271f907..cfa1368 100644
--- a/source/blender/makesrna/intern/rna_access.c
+++ b/source/blender/makesrna/intern/rna_access.c
@@ -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 only if both a valid final pointer and property are found after 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)
 {
@@ -4498,6 +4539,49 @@ char *RNA_path_from_ID_to_property(PointerRNA *ptr, PropertyRNA *prop)
 }
 
 /**
+ * Assumes ptr is not an instance of type!
+ *
+ * \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"]
  */

  • Add null checks (some just-in-case, one crasher)
  • rename RNA_PathElement -> PropertyElemRNA: so it doesn't follow RNA_Struct naming conventions and look like a type.
  • use r_*** prefix for return args.
Updated [P120: Fork of P118 T41062_04.diff](https://archive.blender.org/developer/P120.txt) ``` diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c index 32b073b..458aca4 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.c @@ -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 #41062... */ + 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); } diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h index a2bbaf6..ba2dd8b 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -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); diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index 271f907..cfa1368 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -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 only if both a valid final pointer and property are found after 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) { @@ -4498,6 +4539,49 @@ char *RNA_path_from_ID_to_property(PointerRNA *ptr, PropertyRNA *prop) } /** + * Assumes ptr is not an instance of type! + * + * \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"] */ ``` - Add null checks (some just-in-case, one crasher) - rename `RNA_PathElement` -> `PropertyElemRNA`: so it doesn't follow RNA_Struct naming conventions and look like a type. - use `r_***` prefix for return args.

This issue was referenced by 5336e68e11

This issue was referenced by 5336e68e110252b1da18bf8749b62b6a9f01c10a

Changed status from 'Open' to: 'Resolved'

Changed status from 'Open' to: 'Resolved'

Closed by commit 5336e68e11.

Closed by commit 5336e68e11.
Sign in to join this conversation.
No Label
Interest
Alembic
Interest
Animation & Rigging
Interest
Asset Browser
Interest
Asset Browser Project Overview
Interest
Audio
Interest
Automated Testing
Interest
Blender Asset Bundle
Interest
BlendFile
Interest
Collada
Interest
Compatibility
Interest
Compositing
Interest
Core
Interest
Cycles
Interest
Dependency Graph
Interest
Development Management
Interest
EEVEE
Interest
EEVEE & Viewport
Interest
Freestyle
Interest
Geometry Nodes
Interest
Grease Pencil
Interest
ID Management
Interest
Images & Movies
Interest
Import Export
Interest
Line Art
Interest
Masking
Interest
Metal
Interest
Modeling
Interest
Modifiers
Interest
Motion Tracking
Interest
Nodes & Physics
Interest
OpenGL
Interest
Overlay
Interest
Overrides
Interest
Performance
Interest
Physics
Interest
Pipeline, Assets & IO
Interest
Platforms, Builds & Tests
Interest
Python API
Interest
Render & Cycles
Interest
Render Pipeline
Interest
Sculpt, Paint & Texture
Interest
Text Editor
Interest
Translations
Interest
Triaging
Interest
Undo
Interest
USD
Interest
User Interface
Interest
UV Editing
Interest
VFX & Video
Interest
Video Sequencer
Interest
Virtual Reality
Interest
Vulkan
Interest
Wayland
Interest
Workbench
Interest: X11
Legacy
Blender 2.8 Project
Legacy
Milestone 1: Basic, Local Asset Browser
Legacy
OpenGL Error
Meta
Good First Issue
Meta
Papercut
Meta
Retrospective
Meta
Security
Module
Animation & Rigging
Module
Core
Module
Development Management
Module
EEVEE & Viewport
Module
Grease Pencil
Module
Modeling
Module
Nodes & Physics
Module
Pipeline, Assets & IO
Module
Platforms, Builds & Tests
Module
Python API
Module
Render & Cycles
Module
Sculpt, Paint & Texture
Module
Triaging
Module
User Interface
Module
VFX & Video
Platform
FreeBSD
Platform
Linux
Platform
macOS
Platform
Windows
Priority
High
Priority
Low
Priority
Normal
Priority
Unbreak Now!
Status
Archived
Status
Confirmed
Status
Duplicate
Status
Needs Info from Developers
Status
Needs Information from User
Status
Needs Triage
Status
Resolved
Type
Bug
Type
Design
Type
Known Issue
Type
Patch
Type
Report
Type
To Do
No Milestone
No project
No Assignees
5 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: blender/blender#41062
No description provided.