From baf5e11fc9af5324e4d2461d90b79225804ce517 Mon Sep 17 00:00:00 2001
From: ghidragon <106987263+ghidragon@users.noreply.github.com>
Date: Tue, 9 Apr 2024 18:14:24 -0400
Subject: [PATCH] GP-3871 Added option to remove quotes from string before
being added to clipboard
---
.../help/topics/Tool/ToolOptions_Dialog.htm | 5 +++
.../core/clipboard/ClipboardPlugin.java | 32 ++++++++++++++++++-
.../CodeBrowserClipboardProvider.java | 27 ++++++++--------
.../java/docking/dnd/StringTransferable.java | 11 ++++++-
4 files changed, 60 insertions(+), 15 deletions(-)
diff --git a/Ghidra/Features/Base/src/main/help/help/topics/Tool/ToolOptions_Dialog.htm b/Ghidra/Features/Base/src/main/help/help/topics/Tool/ToolOptions_Dialog.htm
index 97ef8cdc1d..907af02145 100644
--- a/Ghidra/Features/Base/src/main/help/help/topics/Tool/ToolOptions_Dialog.htm
+++ b/Ghidra/Features/Base/src/main/help/help/topics/Tool/ToolOptions_Dialog.htm
@@ -283,7 +283,12 @@
Description |
+
+ Copy Strings Without Quotes |
+ If selected, copying strings to the clipboard from
+ the listing, decompiler, or bytes viewer will remove outer quotes. |
+
Docking Windows On Top |
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/clipboard/ClipboardPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/clipboard/ClipboardPlugin.java
index 8c7e87e3aa..df0a7d8dca 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/clipboard/ClipboardPlugin.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/clipboard/ClipboardPlugin.java
@@ -29,6 +29,8 @@ import javax.swing.event.ChangeListener;
import docking.*;
import docking.action.*;
import docking.dnd.GClipboard;
+import docking.dnd.StringTransferable;
+import docking.tool.ToolConstants;
import generic.theme.GIcon;
import ghidra.app.CorePluginPackage;
import ghidra.app.context.ListingActionContext;
@@ -37,12 +39,15 @@ import ghidra.app.plugin.ProgramPlugin;
import ghidra.app.services.ClipboardContentProviderService;
import ghidra.app.services.ClipboardService;
import ghidra.app.util.ClipboardType;
+import ghidra.framework.options.OptionsChangeListener;
+import ghidra.framework.options.ToolOptions;
import ghidra.framework.plugintool.PluginInfo;
import ghidra.framework.plugintool.PluginTool;
import ghidra.framework.plugintool.util.PluginStatus;
import ghidra.program.model.listing.Program;
import ghidra.util.HelpLocation;
import ghidra.util.Msg;
+import ghidra.util.bean.opteditor.OptionsVetoException;
import ghidra.util.task.*;
//@formatter:off
@@ -55,10 +60,12 @@ import ghidra.util.task.*;
servicesProvided = { ClipboardService.class }
)
//@formatter:on
-public class ClipboardPlugin extends ProgramPlugin implements ClipboardOwner, ClipboardService {
+public class ClipboardPlugin extends ProgramPlugin
+ implements ClipboardOwner, ClipboardService, OptionsChangeListener {
public static final String GROUP_NAME = "Clipboard";
public static final String TOOLBAR_GROUP_NAME = "ZClipboard";
+ private static final String REMOVE_QUOTES_OPTION = "Copy Strings Without Quotes";
//The provider that owns the clipboard content
private ClipboardContentProviderService clipboardOwnerProvider;
@@ -76,9 +83,28 @@ public class ClipboardPlugin extends ProgramPlugin implements ClipboardOwner, Cl
};
private boolean isClipboardOwner;
+ private boolean removeQuotes;
public ClipboardPlugin(PluginTool tool) {
super(tool);
+ initOptions();
+ }
+
+ private void initOptions() {
+ ToolOptions options = tool.getOptions(ToolConstants.TOOL_OPTIONS);
+ options.registerOption(REMOVE_QUOTES_OPTION, false, null,
+ "If true, copying strings to the clipboard will remove outer quotes");
+
+ removeQuotes = options.getBoolean(REMOVE_QUOTES_OPTION, false);
+ options.addOptionsChangeListener(this);
+ }
+
+ @Override
+ public void optionsChanged(ToolOptions options, String optionName, Object oldValue,
+ Object newValue) throws OptionsVetoException {
+ if (optionName.equals(REMOVE_QUOTES_OPTION)) {
+ removeQuotes = options.getBoolean(REMOVE_QUOTES_OPTION, false);
+ }
}
@Override
@@ -246,6 +272,10 @@ public class ClipboardPlugin extends ProgramPlugin implements ClipboardOwner, Cl
monitor.setMessage("Setting Clipboard Contents");
Clipboard systemClipboard = getSystemClipboard();
Transferable transferable = clipboardService.copy(monitor);
+ if (removeQuotes && transferable instanceof StringTransferable stringTransferable) {
+ stringTransferable.removeOuterQuotes();
+ }
+
if (transferable == null) {
return;
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/clipboard/CodeBrowserClipboardProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/clipboard/CodeBrowserClipboardProvider.java
index e88f2d9095..20b386877e 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/clipboard/CodeBrowserClipboardProvider.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/clipboard/CodeBrowserClipboardProvider.java
@@ -30,6 +30,7 @@ import org.apache.commons.lang3.StringUtils;
import docking.ActionContext;
import docking.ComponentProvider;
import docking.dnd.GenericDataFlavor;
+import docking.dnd.StringTransferable;
import docking.widgets.fieldpanel.Layout;
import docking.widgets.fieldpanel.internal.*;
import generic.text.TextLayoutGraphics;
@@ -750,7 +751,7 @@ public class CodeBrowserClipboardProvider extends ByteCopier
// Inner Classes
//==================================================================================================
- private static class LabelStringTransferable implements Transferable {
+ private static class LabelStringTransferable extends StringTransferable {
public static final DataFlavor labelStringFlavor = new GenericDataFlavor(
DataFlavor.javaJVMLocalObjectMimeType + "; class=java.lang.String",
@@ -759,20 +760,18 @@ public class CodeBrowserClipboardProvider extends ByteCopier
private final DataFlavor[] flavors = { labelStringFlavor, DataFlavor.stringFlavor };
private final List flavorList = Arrays.asList(flavors);
- private String symbolName;
-
LabelStringTransferable(String name) {
- this.symbolName = name;
+ super(name);
}
@Override
public Object getTransferData(DataFlavor flavor)
throws UnsupportedFlavorException, IOException {
if (flavor.equals(labelStringFlavor)) {
- return symbolName;
+ return data;
}
if (flavor.equals(DataFlavor.stringFlavor)) {
- return symbolName;
+ return data;
}
throw new UnsupportedFlavorException(flavor);
}
@@ -788,7 +787,7 @@ public class CodeBrowserClipboardProvider extends ByteCopier
}
}
- private static class NonLabelStringTransferable implements Transferable {
+ private static class NonLabelStringTransferable extends StringTransferable {
public static final DataFlavor nonLabelStringFlavor = new GenericDataFlavor(
DataFlavor.javaJVMLocalObjectMimeType + "; class=java.lang.String",
@@ -797,9 +796,11 @@ public class CodeBrowserClipboardProvider extends ByteCopier
private final DataFlavor[] flavors = { nonLabelStringFlavor, DataFlavor.stringFlavor };
private final List flavorList = Arrays.asList(flavors);
- private String text;
-
NonLabelStringTransferable(String[] text) {
+ super(combine(text));
+ }
+
+ private static String combine(String[] text) {
StringBuilder buildy = new StringBuilder();
for (String string : text) {
if (buildy.length() > 0) {
@@ -807,21 +808,21 @@ public class CodeBrowserClipboardProvider extends ByteCopier
}
buildy.append(string);
}
- this.text = buildy.toString();
+ return buildy.toString();
}
NonLabelStringTransferable(String text) {
- this.text = text;
+ super(text);
}
@Override
public Object getTransferData(DataFlavor flavor)
throws UnsupportedFlavorException, IOException {
if (flavor.equals(nonLabelStringFlavor)) {
- return text;
+ return data;
}
if (flavor.equals(DataFlavor.stringFlavor)) {
- return text;
+ return data;
}
throw new UnsupportedFlavorException(flavor);
}
diff --git a/Ghidra/Framework/Docking/src/main/java/docking/dnd/StringTransferable.java b/Ghidra/Framework/Docking/src/main/java/docking/dnd/StringTransferable.java
index a391b6f2ec..d4ce24c680 100644
--- a/Ghidra/Framework/Docking/src/main/java/docking/dnd/StringTransferable.java
+++ b/Ghidra/Framework/Docking/src/main/java/docking/dnd/StringTransferable.java
@@ -20,7 +20,7 @@ import java.io.IOException;
public class StringTransferable implements Transferable {
- private String data = null;
+ protected String data = null;
private DataFlavor[] flavors = { DataFlavor.stringFlavor };
public StringTransferable(String data) {
@@ -42,4 +42,13 @@ public class StringTransferable implements Transferable {
throws UnsupportedFlavorException, IOException {
return data;
}
+
+ public void removeOuterQuotes() {
+ if (data.length() < 2) {
+ return;
+ }
+ if (data.charAt(0) == '"' && data.charAt(data.length() - 1) == '"') {
+ data = data.substring(1, data.length() - 1);
+ }
+ }
}