mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 18:29:37 +02: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:
parent
3051601206
commit
bbe4b89a0d
3 changed files with 87 additions and 58 deletions
|
@ -48,7 +48,6 @@ import ghidra.program.model.symbol.*;
|
|||
import ghidra.program.util.*;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
import ghidra.util.task.TaskMonitorAdapter;
|
||||
|
||||
public class CodeBrowserClipboardProvider extends ByteCopier
|
||||
implements ClipboardContentProviderService {
|
||||
|
@ -211,16 +210,14 @@ public class CodeBrowserClipboardProvider extends ByteCopier
|
|||
|
||||
@Override
|
||||
public List<ClipboardType> getCurrentCopyTypes() {
|
||||
if (copyFromSelectionEnabled) {
|
||||
return COPY_TYPES;
|
||||
}
|
||||
return EMPTY_LIST;
|
||||
return COPY_TYPES;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Transferable copySpecial(ClipboardType copyType, TaskMonitor monitor) {
|
||||
|
||||
if (copyType == ADDRESS_TEXT_TYPE) {
|
||||
return copyAddress(currentSelection.getAddresses(true));
|
||||
return copyAddress();
|
||||
}
|
||||
else if (copyType == CODE_TEXT_TYPE) {
|
||||
return copyCode(monitor);
|
||||
|
@ -235,17 +232,25 @@ public class CodeBrowserClipboardProvider extends ByteCopier
|
|||
return copyLabelsComments(false, true);
|
||||
}
|
||||
else if (copyType == BYTE_STRING_TYPE) {
|
||||
String byteString = copyBytesAsString(currentSelection, true, monitor);
|
||||
String byteString = copyBytesAsString(getSelectedAddresses(), true, monitor);
|
||||
return new ByteViewerTransferable(byteString);
|
||||
}
|
||||
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 null;
|
||||
}
|
||||
|
||||
private AddressSetView getSelectedAddresses() {
|
||||
AddressSetView addressSet = currentSelection;
|
||||
if (addressSet == null || addressSet.isEmpty()) {
|
||||
return new AddressSet(currentLocation.getAddress());
|
||||
}
|
||||
return currentSelection;
|
||||
}
|
||||
|
||||
public void setSelection(ProgramSelection selection) {
|
||||
currentSelection = selection;
|
||||
copyFromSelectionEnabled = selection != null && !selection.isEmpty();
|
||||
|
@ -354,27 +359,32 @@ public class CodeBrowserClipboardProvider extends ByteCopier
|
|||
return new NonLabelStringTransferable(location.getOperandRepresentation());
|
||||
}
|
||||
|
||||
private Transferable copyAddress(AddressIterator addressIterator) {
|
||||
private Transferable copyAddress() {
|
||||
|
||||
AddressSetView addressSet = getSelectedAddresses();
|
||||
StringBuilder buffy = new StringBuilder();
|
||||
while (addressIterator.hasNext()) {
|
||||
buffy.append(addressIterator.next()).append('\n');
|
||||
AddressIterator it = addressSet.getAddresses(true);
|
||||
while (it.hasNext()) {
|
||||
buffy.append(it.next()).append('\n');
|
||||
}
|
||||
return createStringTransferable(buffy.toString());
|
||||
}
|
||||
|
||||
protected Transferable copyCode(TaskMonitor monitor) {
|
||||
|
||||
AddressSetView addressSet = getSelectedAddresses();
|
||||
try {
|
||||
TextLayoutGraphics g = new TextLayoutGraphics();
|
||||
|
||||
Rectangle rect = new Rectangle(2048, 2048);
|
||||
|
||||
AddressRangeIterator rangeItr = currentSelection.getAddressRanges();
|
||||
AddressRangeIterator rangeItr = addressSet.getAddressRanges();
|
||||
while (rangeItr.hasNext()) {
|
||||
AddressRange curRange = rangeItr.next();
|
||||
Address curAddress = curRange.getMinAddress();
|
||||
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) {
|
||||
if (monitor.isCancelled()) {
|
||||
break;
|
||||
|
@ -411,18 +421,19 @@ public class CodeBrowserClipboardProvider extends ByteCopier
|
|||
|
||||
private Transferable copyByteString(Address 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) {
|
||||
|
||||
AddressSetView addressSet = getSelectedAddresses();
|
||||
List<CodeUnitInfo> list = new ArrayList<>();
|
||||
Address startAddr = currentSelection.getMinAddress();
|
||||
getCodeUnitInfo(currentSelection, startAddr, list, copyLabels, copyComments);
|
||||
Address startAddr = addressSet.getMinAddress();
|
||||
getCodeUnitInfo(addressSet, startAddr, list, copyLabels, copyComments);
|
||||
return new CodeUnitInfoTransferable(list);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
// assumed correct data; handled in exception case
|
||||
@SuppressWarnings("unchecked") // assumed correct data; handled in exception case
|
||||
private boolean pasteLabelsComments(Transferable pasteData, boolean pasteLabels,
|
||||
boolean pasteComments) {
|
||||
try {
|
||||
|
@ -627,7 +638,7 @@ public class CodeBrowserClipboardProvider extends ByteCopier
|
|||
|
||||
@Override
|
||||
public boolean canCopySpecial() {
|
||||
return copyFromSelectionEnabled;
|
||||
return currentLocation != null;
|
||||
}
|
||||
|
||||
private boolean canCopyCurrentLocationWithNoSelection() {
|
||||
|
|
|
@ -46,14 +46,6 @@ public class ByteViewerClipboardProvider extends ByteCopier
|
|||
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 pasteEnabled;
|
||||
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.codebrowser.CodeBrowserPlugin;
|
||||
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.DataFormatModel;
|
||||
import ghidra.app.plugin.core.functiongraph.FGClipboardProvider;
|
||||
|
@ -85,7 +86,6 @@ public class ClipboardPluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
private CodeBrowserClipboardProvider codeBrowserClipboardProvider;
|
||||
private CodeBrowserPlugin codeBrowserPlugin;
|
||||
private ByteViewerPlugin byteViewerPlugin;
|
||||
private DecompilePlugin decompilePlugin;
|
||||
private CodeViewerProvider codeViewerProvider;
|
||||
private ClipboardPlugin clipboardPlugin;
|
||||
private ComponentProvider decompileProvider;
|
||||
|
@ -198,7 +198,6 @@ public class ClipboardPluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
codeViewerWrapper = new CodeViewerWrapper(codeViewerProvider);
|
||||
tool.showComponentProvider(codeViewerProvider, true);
|
||||
|
||||
decompilePlugin = getPlugin(tool, DecompilePlugin.class);
|
||||
ComponentProvider decompiler = tool.getComponentProvider("Decompiler");
|
||||
tool.showComponentProvider(decompiler, true);
|
||||
|
||||
|
@ -652,6 +651,43 @@ public class ClipboardPluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
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
|
||||
public void testCopyActionEnablement() {
|
||||
|
||||
|
@ -664,14 +700,14 @@ public class ClipboardPluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
waitForSwing();
|
||||
|
||||
assertTrue(!byteViewerCopyAction.isEnabled());
|
||||
assertTrue(!codeBrowserCopyAction.isEnabled());
|
||||
assertFalse(byteViewerCopyAction.isEnabled());
|
||||
assertFalse(codeBrowserCopyAction.isEnabled());
|
||||
|
||||
// this action is enabled on any text
|
||||
// assertTrue(!decompileCopyAction.isEnabled());
|
||||
// assertFalse(decompileCopyAction.isEnabled());
|
||||
|
||||
// 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
|
||||
// of the cursor is not one of the 'special' copy locations
|
||||
|
@ -720,15 +756,15 @@ public class ClipboardPluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
DockingAction codeBrowserPasteAction =
|
||||
getAction(codeBrowserClipboardProvider, PASTE_ACTION_NAME);
|
||||
|
||||
assertTrue(!byteViewerPasteAction.isEnabled());
|
||||
assertTrue(!codeBrowserPasteAction.isEnabled());
|
||||
assertFalse(byteViewerPasteAction.isEnabled());
|
||||
assertFalse(codeBrowserPasteAction.isEnabled());
|
||||
|
||||
// For each provider make sure no paste on a selection without a copy:
|
||||
// check copy on selection state
|
||||
makeSelection(byteViewerWrapper);
|
||||
|
||||
// check the state
|
||||
assertTrue(!byteViewerPasteAction.isEnabled());
|
||||
assertFalse(byteViewerPasteAction.isEnabled());
|
||||
|
||||
// clear the selection
|
||||
byteViewerWrapper.clearSelection();
|
||||
|
@ -737,7 +773,7 @@ public class ClipboardPluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
codeViewerWrapper.clearSelection();
|
||||
|
||||
// check the state
|
||||
assertTrue(!codeBrowserPasteAction.isEnabled());
|
||||
assertFalse(codeBrowserPasteAction.isEnabled());
|
||||
|
||||
// clear the selection
|
||||
codeViewerWrapper.clearSelection();
|
||||
|
@ -754,7 +790,7 @@ public class ClipboardPluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
copy(byteViewerWrapper, byteViewerCopyAction);
|
||||
|
||||
// check the state
|
||||
assertTrue(!byteViewerPasteAction.isEnabled());
|
||||
assertFalse(byteViewerPasteAction.isEnabled());
|
||||
setByteViewerEditable(true);
|
||||
assertTrue(byteViewerPasteAction.isEnabled());
|
||||
|
||||
|
@ -809,7 +845,7 @@ public class ClipboardPluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
makeSelection(byteViewerWrapper);
|
||||
copy(byteViewerWrapper, byteViewerCopyAction);
|
||||
|
||||
assertTrue(!byteViewerPasteAction.isEnabled());
|
||||
assertFalse(byteViewerPasteAction.isEnabled());
|
||||
setByteViewerEditable(true);
|
||||
assertTrue(byteViewerPasteAction.isEnabled());
|
||||
assertTrue(codeBrowserPasteAction.isEnabled());
|
||||
|
@ -818,8 +854,8 @@ public class ClipboardPluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
clearClipboardContents();
|
||||
|
||||
// sanity check
|
||||
assertTrue(!byteViewerPasteAction.isEnabled());
|
||||
assertTrue(!codeBrowserPasteAction.isEnabled());
|
||||
assertFalse(byteViewerPasteAction.isEnabled());
|
||||
assertFalse(codeBrowserPasteAction.isEnabled());
|
||||
|
||||
// code browser to byte viewer and decompiler
|
||||
makeSelection(codeViewerWrapper);
|
||||
|
@ -848,8 +884,8 @@ public class ClipboardPluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
clearClipboardContents();
|
||||
|
||||
// sanity check
|
||||
assertTrue(!byteViewerPasteAction.isEnabled());
|
||||
assertTrue(!codeBrowserPasteAction.isEnabled());
|
||||
assertFalse(byteViewerPasteAction.isEnabled());
|
||||
assertFalse(codeBrowserPasteAction.isEnabled());
|
||||
|
||||
// copy from decompiler can not paste into the other two
|
||||
makeSelection(decompilerWrapper);
|
||||
|
@ -1427,7 +1463,7 @@ public class ClipboardPluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
}
|
||||
|
||||
private void copySpecial(ComponentProviderWrapper wrapper, final DockingAction copyAction) {
|
||||
wrapper.verifySelection();
|
||||
|
||||
executeOnSwingWithoutBlocking(() -> copyAction.actionPerformed(getActionContext(wrapper)));
|
||||
|
||||
// get the dialog and make a selection
|
||||
|
@ -1445,12 +1481,7 @@ public class ClipboardPluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
okButton.doClick();
|
||||
});
|
||||
|
||||
try {
|
||||
waitForTasks();
|
||||
}
|
||||
catch (Exception e) {
|
||||
Assert.fail();
|
||||
}
|
||||
waitForTasks();
|
||||
}
|
||||
|
||||
private void copySpecial_ByteStringNoSpaces(ComponentProviderWrapper wrapper,
|
||||
|
@ -1473,12 +1504,7 @@ public class ClipboardPluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
okButton.doClick();
|
||||
});
|
||||
|
||||
try {
|
||||
waitForTasks();
|
||||
}
|
||||
catch (Exception e) {
|
||||
Assert.fail("Unexpected exception waiting for tasks");
|
||||
}
|
||||
waitForTasks();
|
||||
}
|
||||
|
||||
private void assertByteViewerBytes(String clipboardContents, Address address)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue