GT-2914 - Decompiler - updated Ctrl-c to copy the symbol under the

cursor when there is no selection
This commit is contained in:
dragonmacher 2019-06-12 10:33:14 -04:00
parent 398db09cec
commit e348f45287
6 changed files with 115 additions and 48 deletions

View file

@ -21,7 +21,7 @@ import javax.swing.JPanel;
import docking.widgets.fieldpanel.support.ViewerPosition;
import ghidra.app.decompiler.DecompileOptions;
import ghidra.app.plugin.core.decompile.DecompileClipboardProvider;
import ghidra.app.plugin.core.decompile.DecompilerClipboardProvider;
import ghidra.app.util.viewer.listingpanel.ProgramLocationListener;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
@ -56,7 +56,7 @@ public class CDisplayPanel extends JPanel implements DecompilerCallbackHandler {
class ExtendedDecompilerController extends DecompilerController {
public ExtendedDecompilerController(DecompilerCallbackHandler handler,
DecompileOptions options, DecompileClipboardProvider clipboard) {
DecompileOptions options, DecompilerClipboardProvider clipboard) {
super(handler, options, clipboard);
}

View file

@ -23,7 +23,7 @@ import com.google.common.cache.CacheBuilder;
import docking.widgets.fieldpanel.support.ViewerPosition;
import ghidra.app.decompiler.*;
import ghidra.app.plugin.core.decompile.DecompileClipboardProvider;
import ghidra.app.plugin.core.decompile.DecompilerClipboardProvider;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.*;
import ghidra.program.model.pcode.HighFunction;
@ -45,7 +45,7 @@ public class DecompilerController {
private int cacheSize;
public DecompilerController(DecompilerCallbackHandler handler, DecompileOptions options,
DecompileClipboardProvider clipboard) {
DecompilerClipboardProvider clipboard) {
this.cacheSize = options.getCacheSize();
this.callbackHandler = handler;
decompilerCache = buildCache();

View file

@ -38,7 +38,7 @@ import docking.widgets.fieldpanel.support.*;
import docking.widgets.indexedscrollpane.IndexedScrollPane;
import ghidra.app.decompiler.*;
import ghidra.app.decompiler.component.hover.DecompilerHoverService;
import ghidra.app.plugin.core.decompile.DecompileClipboardProvider;
import ghidra.app.plugin.core.decompile.DecompilerClipboardProvider;
import ghidra.app.plugin.core.decompile.actions.FieldBasedSearchLocation;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.Function;
@ -74,7 +74,7 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
private SearchLocation currentSearchLocation;
private DecompileData decompileData = new EmptyDecompileData("No Function");
private final DecompileClipboardProvider clipboard;
private final DecompilerClipboardProvider clipboard;
private Color originalBackgroundColor;
private boolean useNonFunctionColor = false;
@ -83,7 +83,7 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
private DecompilerHoverProvider decompilerHoverProvider;
DecompilerPanel(DecompilerController controller, DecompileOptions options,
DecompileClipboardProvider clipboard, JComponent taskMonitorComponent) {
DecompilerClipboardProvider clipboard, JComponent taskMonitorComponent) {
this.controller = controller;
this.options = options;
this.clipboard = clipboard;

View file

@ -15,12 +15,6 @@
*/
package ghidra.app.plugin.core.decompile;
import ghidra.app.decompiler.component.ClangTextField;
import ghidra.app.services.ClipboardContentProviderService;
import ghidra.app.util.ByteCopier;
import ghidra.app.util.ClipboardType;
import ghidra.util.task.TaskMonitor;
import java.awt.FontMetrics;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
@ -31,6 +25,8 @@ import java.util.concurrent.CopyOnWriteArraySet;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.apache.commons.lang3.StringUtils;
import docking.ActionContext;
import docking.ComponentProvider;
import docking.widgets.fieldpanel.Layout;
@ -38,13 +34,20 @@ import docking.widgets.fieldpanel.LayoutModel;
import docking.widgets.fieldpanel.internal.PaintContext;
import docking.widgets.fieldpanel.support.FieldRange;
import docking.widgets.fieldpanel.support.FieldSelection;
import ghidra.app.decompiler.ClangToken;
import ghidra.app.decompiler.component.ClangTextField;
import ghidra.app.decompiler.component.DecompilerPanel;
import ghidra.app.services.ClipboardContentProviderService;
import ghidra.app.util.ByteCopier;
import ghidra.app.util.ClipboardType;
import ghidra.util.task.TaskMonitor;
public class DecompileClipboardProvider extends ByteCopier implements
ClipboardContentProviderService {
public class DecompilerClipboardProvider extends ByteCopier
implements ClipboardContentProviderService {
private static final PaintContext PAINT_CONTEXT = new PaintContext();
private static final ClipboardType TEXT_TYPE = new ClipboardType(DataFlavor.stringFlavor,
"Text");
private static final ClipboardType TEXT_TYPE =
new ClipboardType(DataFlavor.stringFlavor, "Text");
private static final List<ClipboardType> COPY_TYPES = new LinkedList<ClipboardType>();
static {
@ -54,20 +57,22 @@ public class DecompileClipboardProvider extends ByteCopier implements
private DecompilerProvider provider;
private FieldSelection selection;
private boolean copyEnabled;
private boolean copyFromSelectionEnabled;
private Set<ChangeListener> listeners = new CopyOnWriteArraySet<ChangeListener>();
private int spaceCharWidthInPixels = 7;
public DecompileClipboardProvider(DecompilePlugin plugin, DecompilerProvider provider) {
public DecompilerClipboardProvider(DecompilePlugin plugin, DecompilerProvider provider) {
this.provider = provider;
this.tool = plugin.getTool();
PAINT_CONTEXT.setTextCopying(true);
}
@Override
public void addChangeListener(ChangeListener listener) {
listeners.add(listener);
}
@Override
public void removeChangeListener(ChangeListener listener) {
listeners.remove(listener);
}
@ -79,12 +84,29 @@ public class DecompileClipboardProvider extends ByteCopier implements
}
}
@Override
public Transferable copy(TaskMonitor monitor) {
if (!copyFromSelectionEnabled) {
return createStringTransferable(getCursorText());
}
return copyText(monitor);
}
private String getCursorText() {
DecompilerPanel panel = provider.getDecompilerPanel();
ClangToken token = panel.getTokenAtCursor();
if (token == null) {
return null;
}
String text = token.getText();
return text;
}
@Override
public List<ClipboardType> getCurrentCopyTypes() {
if (copyEnabled) {
if (copyFromSelectionEnabled) {
return COPY_TYPES;
}
return EMPTY_LIST;
@ -94,6 +116,7 @@ public class DecompileClipboardProvider extends ByteCopier implements
return null;
}
@Override
public Transferable copySpecial(ClipboardType copyType, TaskMonitor monitor) {
if (copyType == TEXT_TYPE) {
return copyText(monitor);
@ -102,30 +125,35 @@ public class DecompileClipboardProvider extends ByteCopier implements
return null;
}
@Override
public boolean isValidContext(ActionContext context) {
return context.getComponentProvider() == provider;
}
public void selectionChanged(FieldSelection sel) {
this.selection = sel;
copyEnabled = (selection != null && selection.getNumRanges() > 0);
copyFromSelectionEnabled = (selection != null && selection.getNumRanges() > 0);
notifyStateChanged();
}
@Override
public ComponentProvider getComponentProvider() {
return provider;
}
@Override
public boolean enableCopy() {
return true;
}
@Override
public boolean enableCopySpecial() {
return false;
}
@Override
public boolean canCopy() {
return copyEnabled;
return copyFromSelectionEnabled || !StringUtils.isBlank(getCursorText());
}
@Override
@ -134,26 +162,6 @@ public class DecompileClipboardProvider extends ByteCopier implements
}
protected Transferable copyText(TaskMonitor monitor) {
// TextLayoutGraphics g = new TextLayoutGraphics();
// Rectangle rect = new Rectangle(2048, 2048);
// LayoutBackgroundColorManager layoutColorMap = new EmptyLayoutBackgroundColorManager(PAINT_CONTEXT.background);
//
// LayoutModel model = provider.getDecompilerPanel().getLayoutModel();
// int numRanges = selection.getNumRanges();
// for (int i=0; i<numRanges; i++) {
// FieldRange range = selection.getFieldRange(i);
// for (int j=range.getStart().getIndex(); j<range.getEnd().getIndex(); j++) {
// Layout layout = model.getLayout(j);
// if (layout != null) {
// layout.paint(g, PAINT_CONTEXT, rect, layoutColorMap, null);
// g.setHeight(layout.getHeight());
// g.flush();
// }
// }
// }
//
// return createStringTransferable(g.getBuffer().toString());
return createStringTransferable(getText());
}
@ -173,9 +181,7 @@ public class DecompileClipboardProvider extends ByteCopier implements
appendTextSingleLine(buffer, startIndex, selection.intersect(startIndex));
return;
}
// if (fieldRange.getEnd().getCol() == 0) {
// endIndex--;
// }
appendText(buffer, startIndex, selection.intersect(startIndex));
for (int line = startIndex + 1; line <= endIndex; line++) {
buffer.append('\n');
@ -183,7 +189,8 @@ public class DecompileClipboardProvider extends ByteCopier implements
}
}
private void appendText(StringBuffer buffer, int lineNumber, FieldSelection singleLineSelection) {
private void appendText(StringBuffer buffer, int lineNumber,
FieldSelection singleLineSelection) {
if (singleLineSelection.isEmpty()) {
return;
}
@ -244,14 +251,17 @@ public class DecompileClipboardProvider extends ByteCopier implements
// Unsupported Operations
//==================================================================================================
@Override
public boolean enablePaste() {
return false;
}
@Override
public boolean canPaste(DataFlavor[] availableFlavors) {
return false;
}
@Override
public boolean paste(Transferable pasteData) {
return false;
}
@ -260,6 +270,7 @@ public class DecompileClipboardProvider extends ByteCopier implements
return false;
}
@Override
public void lostOwnership(Transferable transferable) {
// no-op
}

View file

@ -86,7 +86,7 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
private final DecompilePlugin plugin;
private ClipboardService clipboardService;
private DecompileClipboardProvider clipboardProvider;
private DecompilerClipboardProvider clipboardProvider;
private DecompileOptions decompilerOptions;
private Program program;
@ -124,7 +124,7 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
public DecompilerProvider(DecompilePlugin plugin, boolean isConnected) {
super(plugin.getTool(), "Decompiler", plugin.getName(), DecompilerActionContext.class);
this.plugin = plugin;
clipboardProvider = new DecompileClipboardProvider(plugin, this);
clipboardProvider = new DecompilerClipboardProvider(plugin, this);
setConnected(isConnected);
decompilerOptions = new DecompileOptions();

View file

@ -21,6 +21,7 @@ import java.util.List;
import org.junit.Test;
import docking.action.DockingActionIf;
import docking.widgets.fieldpanel.field.Field;
import docking.widgets.fieldpanel.support.FieldLocation;
import ghidra.app.cmd.comments.SetCommentCmd;
@ -111,10 +112,65 @@ public class DecompilerClangTest extends AbstractDecompilerTest {
assertCurrentLocation(line, charPosition);
}
@Test
public void testDecompiler_CopyFromSymbolWithoutSelection() throws Exception {
/*
Decomp of '_call_structure_A':
1|
2| void _call_structure_A(A *a)
3|
4| {
5| _printf("call_structure_A: %s\n",a->name);
6| _printf("call_structure_A: %s\n",(a->b).name);
7| _printf("call_structure_A: %s\n",(a->b).c.name);
8| _printf("call_structure_A: %s\n",(a->b).c.d.name);
9| _printf("call_structure_A: %s\n",(a->b).c.d.e.name);
10| _call_structure_B(&a->b);
11| return;
12| }
*/
decompile("100000d60"); // '_call_structure_A'
int line = 2; // void
int charPosition = 2;
setDecompilerLocation(line, charPosition);
copy();
String copiedText = getClipboardText();
assertEquals("void", copiedText);
line = 5; // _printf
charPosition = 2; //
setDecompilerLocation(line, charPosition);
copy();
copiedText = getClipboardText();
assertEquals("_printf", copiedText);
}
//==================================================================================================
// Private Methods
//==================================================================================================
private void copy() {
String fullName = "Copy (ClipboardPlugin)";
List<DockingActionIf> actions = tool.getDockingActionsByFullActionName(fullName);
for (DockingActionIf action : actions) {
Object service = getInstanceField("clipboardService", action);
if (service.getClass().toString().contains("Decomp")) {
performAction(action);
return;
}
}
fail("Could not find Decompiler Copy action");
}
private void setComment(String address, int type, String comment) {
applyCmd(program, new SetCommentCmd(addr(address), type, comment));
}