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