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:
dragonmacher 2019-10-30 11:46:30 -04:00
parent 3051601206
commit bbe4b89a0d
3 changed files with 87 additions and 58 deletions

View file

@ -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() {

View file

@ -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<>();

View file

@ -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)