mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-09-28 17:11:11 -09:00
GT-3155 - Copy Special - allow the Copy Special action to work on the
current address when there is no selection
This commit is contained in:
@@ -48,7 +48,6 @@ import ghidra.program.model.symbol.*;
|
|||||||
import ghidra.program.util.*;
|
import ghidra.program.util.*;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
import ghidra.util.task.TaskMonitorAdapter;
|
|
||||||
|
|
||||||
public class CodeBrowserClipboardProvider extends ByteCopier
|
public class CodeBrowserClipboardProvider extends ByteCopier
|
||||||
implements ClipboardContentProviderService {
|
implements ClipboardContentProviderService {
|
||||||
@@ -211,16 +210,14 @@ public class CodeBrowserClipboardProvider extends ByteCopier
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<ClipboardType> getCurrentCopyTypes() {
|
public List<ClipboardType> getCurrentCopyTypes() {
|
||||||
if (copyFromSelectionEnabled) {
|
return COPY_TYPES;
|
||||||
return COPY_TYPES;
|
|
||||||
}
|
|
||||||
return EMPTY_LIST;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Transferable copySpecial(ClipboardType copyType, TaskMonitor monitor) {
|
public Transferable copySpecial(ClipboardType copyType, TaskMonitor monitor) {
|
||||||
|
|
||||||
if (copyType == ADDRESS_TEXT_TYPE) {
|
if (copyType == ADDRESS_TEXT_TYPE) {
|
||||||
return copyAddress(currentSelection.getAddresses(true));
|
return copyAddress();
|
||||||
}
|
}
|
||||||
else if (copyType == CODE_TEXT_TYPE) {
|
else if (copyType == CODE_TEXT_TYPE) {
|
||||||
return copyCode(monitor);
|
return copyCode(monitor);
|
||||||
@@ -235,17 +232,25 @@ public class CodeBrowserClipboardProvider extends ByteCopier
|
|||||||
return copyLabelsComments(false, true);
|
return copyLabelsComments(false, true);
|
||||||
}
|
}
|
||||||
else if (copyType == BYTE_STRING_TYPE) {
|
else if (copyType == BYTE_STRING_TYPE) {
|
||||||
String byteString = copyBytesAsString(currentSelection, true, monitor);
|
String byteString = copyBytesAsString(getSelectedAddresses(), true, monitor);
|
||||||
return new ByteViewerTransferable(byteString);
|
return new ByteViewerTransferable(byteString);
|
||||||
}
|
}
|
||||||
else if (copyType == BYTE_STRING_NO_SPACE_TYPE) {
|
else if (copyType == BYTE_STRING_NO_SPACE_TYPE) {
|
||||||
String byteString = copyBytesAsString(currentSelection, false, monitor);
|
String byteString = copyBytesAsString(getSelectedAddresses(), false, monitor);
|
||||||
return new ByteViewerTransferable(byteString);
|
return new ByteViewerTransferable(byteString);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private AddressSetView getSelectedAddresses() {
|
||||||
|
AddressSetView addressSet = currentSelection;
|
||||||
|
if (addressSet == null || addressSet.isEmpty()) {
|
||||||
|
return new AddressSet(currentLocation.getAddress());
|
||||||
|
}
|
||||||
|
return currentSelection;
|
||||||
|
}
|
||||||
|
|
||||||
public void setSelection(ProgramSelection selection) {
|
public void setSelection(ProgramSelection selection) {
|
||||||
currentSelection = selection;
|
currentSelection = selection;
|
||||||
copyFromSelectionEnabled = selection != null && !selection.isEmpty();
|
copyFromSelectionEnabled = selection != null && !selection.isEmpty();
|
||||||
@@ -354,27 +359,32 @@ public class CodeBrowserClipboardProvider extends ByteCopier
|
|||||||
return new NonLabelStringTransferable(location.getOperandRepresentation());
|
return new NonLabelStringTransferable(location.getOperandRepresentation());
|
||||||
}
|
}
|
||||||
|
|
||||||
private Transferable copyAddress(AddressIterator addressIterator) {
|
private Transferable copyAddress() {
|
||||||
|
|
||||||
|
AddressSetView addressSet = getSelectedAddresses();
|
||||||
StringBuilder buffy = new StringBuilder();
|
StringBuilder buffy = new StringBuilder();
|
||||||
while (addressIterator.hasNext()) {
|
AddressIterator it = addressSet.getAddresses(true);
|
||||||
buffy.append(addressIterator.next()).append('\n');
|
while (it.hasNext()) {
|
||||||
|
buffy.append(it.next()).append('\n');
|
||||||
}
|
}
|
||||||
return createStringTransferable(buffy.toString());
|
return createStringTransferable(buffy.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Transferable copyCode(TaskMonitor monitor) {
|
protected Transferable copyCode(TaskMonitor monitor) {
|
||||||
|
|
||||||
|
AddressSetView addressSet = getSelectedAddresses();
|
||||||
try {
|
try {
|
||||||
TextLayoutGraphics g = new TextLayoutGraphics();
|
TextLayoutGraphics g = new TextLayoutGraphics();
|
||||||
|
|
||||||
Rectangle rect = new Rectangle(2048, 2048);
|
Rectangle rect = new Rectangle(2048, 2048);
|
||||||
|
|
||||||
AddressRangeIterator rangeItr = currentSelection.getAddressRanges();
|
AddressRangeIterator rangeItr = addressSet.getAddressRanges();
|
||||||
while (rangeItr.hasNext()) {
|
while (rangeItr.hasNext()) {
|
||||||
AddressRange curRange = rangeItr.next();
|
AddressRange curRange = rangeItr.next();
|
||||||
Address curAddress = curRange.getMinAddress();
|
Address curAddress = curRange.getMinAddress();
|
||||||
Address maxAddress = curRange.getMaxAddress();
|
Address maxAddress = curRange.getMaxAddress();
|
||||||
// check curAddress against null because getAddressAfter(curAddress) returns null
|
|
||||||
// in certain situations
|
// getAddressAfter(curAddress) returns null in certain situations
|
||||||
while (curAddress != null && curAddress.compareTo(maxAddress) <= 0) {
|
while (curAddress != null && curAddress.compareTo(maxAddress) <= 0) {
|
||||||
if (monitor.isCancelled()) {
|
if (monitor.isCancelled()) {
|
||||||
break;
|
break;
|
||||||
@@ -411,18 +421,19 @@ public class CodeBrowserClipboardProvider extends ByteCopier
|
|||||||
|
|
||||||
private Transferable copyByteString(Address address) {
|
private Transferable copyByteString(Address address) {
|
||||||
AddressSet set = new AddressSet(address);
|
AddressSet set = new AddressSet(address);
|
||||||
return copyBytes(set, false, TaskMonitorAdapter.DUMMY_MONITOR);
|
return copyBytes(set, false, TaskMonitor.DUMMY);
|
||||||
}
|
}
|
||||||
|
|
||||||
private CodeUnitInfoTransferable copyLabelsComments(boolean copyLabels, boolean copyComments) {
|
private CodeUnitInfoTransferable copyLabelsComments(boolean copyLabels, boolean copyComments) {
|
||||||
|
|
||||||
|
AddressSetView addressSet = getSelectedAddresses();
|
||||||
List<CodeUnitInfo> list = new ArrayList<>();
|
List<CodeUnitInfo> list = new ArrayList<>();
|
||||||
Address startAddr = currentSelection.getMinAddress();
|
Address startAddr = addressSet.getMinAddress();
|
||||||
getCodeUnitInfo(currentSelection, startAddr, list, copyLabels, copyComments);
|
getCodeUnitInfo(addressSet, startAddr, list, copyLabels, copyComments);
|
||||||
return new CodeUnitInfoTransferable(list);
|
return new CodeUnitInfoTransferable(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked") // assumed correct data; handled in exception case
|
||||||
// assumed correct data; handled in exception case
|
|
||||||
private boolean pasteLabelsComments(Transferable pasteData, boolean pasteLabels,
|
private boolean pasteLabelsComments(Transferable pasteData, boolean pasteLabels,
|
||||||
boolean pasteComments) {
|
boolean pasteComments) {
|
||||||
try {
|
try {
|
||||||
@@ -627,7 +638,7 @@ public class CodeBrowserClipboardProvider extends ByteCopier
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canCopySpecial() {
|
public boolean canCopySpecial() {
|
||||||
return copyFromSelectionEnabled;
|
return currentLocation != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean canCopyCurrentLocationWithNoSelection() {
|
private boolean canCopyCurrentLocationWithNoSelection() {
|
||||||
|
|||||||
@@ -46,14 +46,6 @@ public class ByteViewerClipboardProvider extends ByteCopier
|
|||||||
return copyTypesList;
|
return copyTypesList;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final List<ClipboardType> PASTE_TYPES = createPasteTypesList();
|
|
||||||
|
|
||||||
private static List<ClipboardType> createPasteTypesList() {
|
|
||||||
List<ClipboardType> pasteTypesList = new LinkedList<>();
|
|
||||||
pasteTypesList.add(BYTE_STRING_TYPE);
|
|
||||||
return pasteTypesList;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean copyEnabled;
|
private boolean copyEnabled;
|
||||||
private boolean pasteEnabled;
|
private boolean pasteEnabled;
|
||||||
private Set<ChangeListener> listeners = new CopyOnWriteArraySet<>();
|
private Set<ChangeListener> listeners = new CopyOnWriteArraySet<>();
|
||||||
|
|||||||
@@ -42,7 +42,8 @@ import ghidra.app.plugin.core.byteviewer.*;
|
|||||||
import ghidra.app.plugin.core.clear.ClearCmd;
|
import ghidra.app.plugin.core.clear.ClearCmd;
|
||||||
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
||||||
import ghidra.app.plugin.core.codebrowser.CodeViewerProvider;
|
import ghidra.app.plugin.core.codebrowser.CodeViewerProvider;
|
||||||
import ghidra.app.plugin.core.decompile.*;
|
import ghidra.app.plugin.core.decompile.DecompilerClipboardProvider;
|
||||||
|
import ghidra.app.plugin.core.decompile.DecompilerProvider;
|
||||||
import ghidra.app.plugin.core.format.ByteBlockSelection;
|
import ghidra.app.plugin.core.format.ByteBlockSelection;
|
||||||
import ghidra.app.plugin.core.format.DataFormatModel;
|
import ghidra.app.plugin.core.format.DataFormatModel;
|
||||||
import ghidra.app.plugin.core.functiongraph.FGClipboardProvider;
|
import ghidra.app.plugin.core.functiongraph.FGClipboardProvider;
|
||||||
@@ -85,7 +86,6 @@ public class ClipboardPluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||||||
private CodeBrowserClipboardProvider codeBrowserClipboardProvider;
|
private CodeBrowserClipboardProvider codeBrowserClipboardProvider;
|
||||||
private CodeBrowserPlugin codeBrowserPlugin;
|
private CodeBrowserPlugin codeBrowserPlugin;
|
||||||
private ByteViewerPlugin byteViewerPlugin;
|
private ByteViewerPlugin byteViewerPlugin;
|
||||||
private DecompilePlugin decompilePlugin;
|
|
||||||
private CodeViewerProvider codeViewerProvider;
|
private CodeViewerProvider codeViewerProvider;
|
||||||
private ClipboardPlugin clipboardPlugin;
|
private ClipboardPlugin clipboardPlugin;
|
||||||
private ComponentProvider decompileProvider;
|
private ComponentProvider decompileProvider;
|
||||||
@@ -198,7 +198,6 @@ public class ClipboardPluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||||||
codeViewerWrapper = new CodeViewerWrapper(codeViewerProvider);
|
codeViewerWrapper = new CodeViewerWrapper(codeViewerProvider);
|
||||||
tool.showComponentProvider(codeViewerProvider, true);
|
tool.showComponentProvider(codeViewerProvider, true);
|
||||||
|
|
||||||
decompilePlugin = getPlugin(tool, DecompilePlugin.class);
|
|
||||||
ComponentProvider decompiler = tool.getComponentProvider("Decompiler");
|
ComponentProvider decompiler = tool.getComponentProvider("Decompiler");
|
||||||
tool.showComponentProvider(decompiler, true);
|
tool.showComponentProvider(decompiler, true);
|
||||||
|
|
||||||
@@ -652,6 +651,43 @@ public class ClipboardPluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||||||
assertEquals(commentText, comments[0]);
|
assertEquals(commentText, comments[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCodeBrowser_CopySpecial_WithSelection() throws Exception {
|
||||||
|
|
||||||
|
DockingAction copySpecialAction =
|
||||||
|
getAction(codeBrowserClipboardProvider, COPY_SPECIAL_ACTION_NAME);
|
||||||
|
waitForSwing();
|
||||||
|
assertFalse(copySpecialAction.isEnabled());
|
||||||
|
|
||||||
|
codeBrowserPlugin.goTo(new MnemonicFieldLocation(program, addr("1001050")));
|
||||||
|
assertTrue(copySpecialAction.isEnabled());
|
||||||
|
|
||||||
|
makeSelection(codeViewerWrapper);
|
||||||
|
assertTrue(copySpecialAction.isEnabled());
|
||||||
|
|
||||||
|
copySpecial(codeViewerWrapper, copySpecialAction);
|
||||||
|
String clipboardContents = getClipboardContents();
|
||||||
|
String expectedBytes = "f4 77 33 58 f4 77 91 45";
|
||||||
|
assertEquals(expectedBytes, clipboardContents);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCodeBrowser_CopySpecial_WithoutSelection() throws Exception {
|
||||||
|
|
||||||
|
DockingAction copySpecialAction =
|
||||||
|
getAction(codeBrowserClipboardProvider, COPY_SPECIAL_ACTION_NAME);
|
||||||
|
waitForSwing();
|
||||||
|
assertFalse(copySpecialAction.isEnabled());
|
||||||
|
|
||||||
|
codeBrowserPlugin.goTo(new MnemonicFieldLocation(program, addr("1001050")));
|
||||||
|
assertTrue(copySpecialAction.isEnabled());
|
||||||
|
|
||||||
|
copySpecial(codeViewerWrapper, copySpecialAction);
|
||||||
|
String clipboardContents = getClipboardContents();
|
||||||
|
String expectedBytes = "e0";
|
||||||
|
assertEquals(expectedBytes, clipboardContents);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCopyActionEnablement() {
|
public void testCopyActionEnablement() {
|
||||||
|
|
||||||
@@ -664,14 +700,14 @@ public class ClipboardPluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||||||
|
|
||||||
waitForSwing();
|
waitForSwing();
|
||||||
|
|
||||||
assertTrue(!byteViewerCopyAction.isEnabled());
|
assertFalse(byteViewerCopyAction.isEnabled());
|
||||||
assertTrue(!codeBrowserCopyAction.isEnabled());
|
assertFalse(codeBrowserCopyAction.isEnabled());
|
||||||
|
|
||||||
// this action is enabled on any text
|
// this action is enabled on any text
|
||||||
// assertTrue(!decompileCopyAction.isEnabled());
|
// assertFalse(decompileCopyAction.isEnabled());
|
||||||
|
|
||||||
// give the providers focus and check their actions state
|
// give the providers focus and check their actions state
|
||||||
assertTrue(!byteViewerCopyAction.isEnabled());
|
assertFalse(byteViewerCopyAction.isEnabled());
|
||||||
|
|
||||||
// the code browser is special--make sure that it not only has focus, but that the location
|
// the code browser is special--make sure that it not only has focus, but that the location
|
||||||
// of the cursor is not one of the 'special' copy locations
|
// of the cursor is not one of the 'special' copy locations
|
||||||
@@ -720,15 +756,15 @@ public class ClipboardPluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||||||
DockingAction codeBrowserPasteAction =
|
DockingAction codeBrowserPasteAction =
|
||||||
getAction(codeBrowserClipboardProvider, PASTE_ACTION_NAME);
|
getAction(codeBrowserClipboardProvider, PASTE_ACTION_NAME);
|
||||||
|
|
||||||
assertTrue(!byteViewerPasteAction.isEnabled());
|
assertFalse(byteViewerPasteAction.isEnabled());
|
||||||
assertTrue(!codeBrowserPasteAction.isEnabled());
|
assertFalse(codeBrowserPasteAction.isEnabled());
|
||||||
|
|
||||||
// For each provider make sure no paste on a selection without a copy:
|
// For each provider make sure no paste on a selection without a copy:
|
||||||
// check copy on selection state
|
// check copy on selection state
|
||||||
makeSelection(byteViewerWrapper);
|
makeSelection(byteViewerWrapper);
|
||||||
|
|
||||||
// check the state
|
// check the state
|
||||||
assertTrue(!byteViewerPasteAction.isEnabled());
|
assertFalse(byteViewerPasteAction.isEnabled());
|
||||||
|
|
||||||
// clear the selection
|
// clear the selection
|
||||||
byteViewerWrapper.clearSelection();
|
byteViewerWrapper.clearSelection();
|
||||||
@@ -737,7 +773,7 @@ public class ClipboardPluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||||||
codeViewerWrapper.clearSelection();
|
codeViewerWrapper.clearSelection();
|
||||||
|
|
||||||
// check the state
|
// check the state
|
||||||
assertTrue(!codeBrowserPasteAction.isEnabled());
|
assertFalse(codeBrowserPasteAction.isEnabled());
|
||||||
|
|
||||||
// clear the selection
|
// clear the selection
|
||||||
codeViewerWrapper.clearSelection();
|
codeViewerWrapper.clearSelection();
|
||||||
@@ -754,7 +790,7 @@ public class ClipboardPluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||||||
copy(byteViewerWrapper, byteViewerCopyAction);
|
copy(byteViewerWrapper, byteViewerCopyAction);
|
||||||
|
|
||||||
// check the state
|
// check the state
|
||||||
assertTrue(!byteViewerPasteAction.isEnabled());
|
assertFalse(byteViewerPasteAction.isEnabled());
|
||||||
setByteViewerEditable(true);
|
setByteViewerEditable(true);
|
||||||
assertTrue(byteViewerPasteAction.isEnabled());
|
assertTrue(byteViewerPasteAction.isEnabled());
|
||||||
|
|
||||||
@@ -809,7 +845,7 @@ public class ClipboardPluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||||||
makeSelection(byteViewerWrapper);
|
makeSelection(byteViewerWrapper);
|
||||||
copy(byteViewerWrapper, byteViewerCopyAction);
|
copy(byteViewerWrapper, byteViewerCopyAction);
|
||||||
|
|
||||||
assertTrue(!byteViewerPasteAction.isEnabled());
|
assertFalse(byteViewerPasteAction.isEnabled());
|
||||||
setByteViewerEditable(true);
|
setByteViewerEditable(true);
|
||||||
assertTrue(byteViewerPasteAction.isEnabled());
|
assertTrue(byteViewerPasteAction.isEnabled());
|
||||||
assertTrue(codeBrowserPasteAction.isEnabled());
|
assertTrue(codeBrowserPasteAction.isEnabled());
|
||||||
@@ -818,8 +854,8 @@ public class ClipboardPluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||||||
clearClipboardContents();
|
clearClipboardContents();
|
||||||
|
|
||||||
// sanity check
|
// sanity check
|
||||||
assertTrue(!byteViewerPasteAction.isEnabled());
|
assertFalse(byteViewerPasteAction.isEnabled());
|
||||||
assertTrue(!codeBrowserPasteAction.isEnabled());
|
assertFalse(codeBrowserPasteAction.isEnabled());
|
||||||
|
|
||||||
// code browser to byte viewer and decompiler
|
// code browser to byte viewer and decompiler
|
||||||
makeSelection(codeViewerWrapper);
|
makeSelection(codeViewerWrapper);
|
||||||
@@ -848,8 +884,8 @@ public class ClipboardPluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||||||
clearClipboardContents();
|
clearClipboardContents();
|
||||||
|
|
||||||
// sanity check
|
// sanity check
|
||||||
assertTrue(!byteViewerPasteAction.isEnabled());
|
assertFalse(byteViewerPasteAction.isEnabled());
|
||||||
assertTrue(!codeBrowserPasteAction.isEnabled());
|
assertFalse(codeBrowserPasteAction.isEnabled());
|
||||||
|
|
||||||
// copy from decompiler can not paste into the other two
|
// copy from decompiler can not paste into the other two
|
||||||
makeSelection(decompilerWrapper);
|
makeSelection(decompilerWrapper);
|
||||||
@@ -1427,7 +1463,7 @@ public class ClipboardPluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void copySpecial(ComponentProviderWrapper wrapper, final DockingAction copyAction) {
|
private void copySpecial(ComponentProviderWrapper wrapper, final DockingAction copyAction) {
|
||||||
wrapper.verifySelection();
|
|
||||||
executeOnSwingWithoutBlocking(() -> copyAction.actionPerformed(getActionContext(wrapper)));
|
executeOnSwingWithoutBlocking(() -> copyAction.actionPerformed(getActionContext(wrapper)));
|
||||||
|
|
||||||
// get the dialog and make a selection
|
// get the dialog and make a selection
|
||||||
@@ -1445,12 +1481,7 @@ public class ClipboardPluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||||||
okButton.doClick();
|
okButton.doClick();
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
waitForTasks();
|
||||||
waitForTasks();
|
|
||||||
}
|
|
||||||
catch (Exception e) {
|
|
||||||
Assert.fail();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void copySpecial_ByteStringNoSpaces(ComponentProviderWrapper wrapper,
|
private void copySpecial_ByteStringNoSpaces(ComponentProviderWrapper wrapper,
|
||||||
@@ -1473,12 +1504,7 @@ public class ClipboardPluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||||||
okButton.doClick();
|
okButton.doClick();
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
waitForTasks();
|
||||||
waitForTasks();
|
|
||||||
}
|
|
||||||
catch (Exception e) {
|
|
||||||
Assert.fail("Unexpected exception waiting for tasks");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertByteViewerBytes(String clipboardContents, Address address)
|
private void assertByteViewerBytes(String clipboardContents, Address address)
|
||||||
|
|||||||
Reference in New Issue
Block a user