mirror of
https://github.com/NationalSecurityAgency/ghidra
synced 2024-08-28 13:30:21 +00:00
GP-2446: Add margin provider to decompiler. Port line numbers to it.
This commit is contained in:
parent
7e24c986ad
commit
e72aa6e039
|
@ -0,0 +1,37 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.decompiler;
|
||||
|
||||
import ghidra.app.decompiler.component.margin.DecompilerMarginProvider;
|
||||
|
||||
/**
|
||||
* A service that allows clients to add custom margins in the Decompiler UI.
|
||||
*/
|
||||
public interface DecompilerMarginService {
|
||||
/**
|
||||
* Add a margin to the Decompiler's primary window
|
||||
*
|
||||
* @param provider the margin provider
|
||||
*/
|
||||
void addMarginProvider(DecompilerMarginProvider provider);
|
||||
|
||||
/**
|
||||
* Remove a margin from the Decompiler's primary window
|
||||
*
|
||||
* @param provider the margin provider
|
||||
*/
|
||||
void removeMarginProvider(DecompilerMarginProvider provider);
|
||||
}
|
|
@ -159,7 +159,7 @@ public class CDisplayPanel extends JPanel implements DecompilerCallbackHandler {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void doWheNotBusy(Callback c) {
|
||||
public void doWhenNotBusy(Callback c) {
|
||||
// stub
|
||||
}
|
||||
|
||||
|
|
|
@ -21,8 +21,6 @@ import java.util.ArrayList;
|
|||
import java.util.List;
|
||||
import java.util.regex.*;
|
||||
|
||||
import javax.swing.JComponent;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import docking.widgets.SearchLocation;
|
||||
|
@ -47,10 +45,7 @@ import ghidra.util.Msg;
|
|||
*/
|
||||
public class ClangLayoutController implements LayoutModel, LayoutModelListener {
|
||||
|
||||
private final ClangFieldElement EMPTY_LINE_NUMBER_SPACER;
|
||||
|
||||
private int maxWidth;
|
||||
private int lineNumberFieldWidth;
|
||||
private int indentWidth;
|
||||
private DecompileOptions options;
|
||||
private DecompilerPanel decompilerPanel;
|
||||
|
@ -59,26 +54,19 @@ public class ClangLayoutController implements LayoutModel, LayoutModelListener {
|
|||
private FontMetrics metrics;
|
||||
private HighlightFactory hlFactory;
|
||||
private ArrayList<LayoutModelListener> listeners;
|
||||
private Color[] syntax_color; // Foreground colors.
|
||||
private Color[] syntaxColor; // Foreground colors.
|
||||
private BigInteger numIndexes = BigInteger.ZERO;
|
||||
private ArrayList<ClangLine> lines = new ArrayList<>();
|
||||
|
||||
private boolean showLineNumbers = true;
|
||||
|
||||
private ClangFieldElement createEmptyLineNumberSpacer() {
|
||||
ClangToken lineNumberToken = ClangToken.buildSpacer(null, 0, "");
|
||||
AttributedString as = new AttributedString("", Color.WHITE, metrics);
|
||||
return new ClangFieldElement(lineNumberToken, as, 0);
|
||||
}
|
||||
|
||||
public ClangLayoutController(DecompileOptions opt, DecompilerPanel decompilerPanel,
|
||||
FontMetrics met, HighlightFactory hlFactory) {
|
||||
options = opt;
|
||||
this.decompilerPanel = decompilerPanel;
|
||||
syntax_color = new Color[ClangToken.MAX_COLOR];
|
||||
syntaxColor = new Color[ClangToken.MAX_COLOR];
|
||||
metrics = met;
|
||||
this.hlFactory = hlFactory;
|
||||
EMPTY_LINE_NUMBER_SPACER = createEmptyLineNumberSpacer();
|
||||
listeners = new ArrayList<>();
|
||||
buildLayouts(null, null, null, false);
|
||||
}
|
||||
|
@ -94,7 +82,7 @@ public class ClangLayoutController implements LayoutModel, LayoutModelListener {
|
|||
|
||||
@Override
|
||||
public Dimension getPreferredViewSize() {
|
||||
return new Dimension(maxWidth + lineNumberFieldWidth, 500);
|
||||
return new Dimension(maxWidth, 500);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -179,15 +167,11 @@ public class ClangLayoutController implements LayoutModel, LayoutModelListener {
|
|||
boolean paintLineNumbers) {
|
||||
List<ClangToken> tokens = line.getAllTokens();
|
||||
|
||||
ClangFieldElement lineNumberFieldElement =
|
||||
createLineNumberFieldElement(line, lineCount, paintLineNumbers);
|
||||
|
||||
FieldElement[] elements = createFieldElementsForLine(tokens);
|
||||
|
||||
int indent = line.getIndent() * indentWidth;
|
||||
int lineNumberWidth = lineNumberFieldElement.getStringWidth();
|
||||
int updatedMaxWidth = maxWidth + lineNumberWidth;
|
||||
return new ClangTextField(tokens, elements, lineNumberFieldElement, indent, updatedMaxWidth,
|
||||
int updatedMaxWidth = maxWidth;
|
||||
return new ClangTextField(tokens, elements, indent, line.getLineNumber(), updatedMaxWidth,
|
||||
hlFactory);
|
||||
}
|
||||
|
||||
|
@ -197,7 +181,7 @@ public class ClangLayoutController implements LayoutModel, LayoutModelListener {
|
|||
int columnPosition = 0;
|
||||
for (int i = 0; i < tokens.size(); ++i) {
|
||||
ClangToken token = tokens.get(i);
|
||||
Color color = syntax_color[token.getSyntaxType()];
|
||||
Color color = syntaxColor[token.getSyntaxType()];
|
||||
if (token instanceof ClangCommentToken) {
|
||||
AttributedString prototype = new AttributedString("prototype", color, metrics);
|
||||
Program program = decompilerPanel.getProgram();
|
||||
|
@ -214,32 +198,22 @@ public class ClangLayoutController implements LayoutModel, LayoutModelListener {
|
|||
return elements;
|
||||
}
|
||||
|
||||
private ClangFieldElement createLineNumberFieldElement(ClangLine line, int lineCount,
|
||||
boolean paintLineNumbers) {
|
||||
|
||||
if (paintLineNumbers) {
|
||||
return new LineNumberFieldElement(line.getLineNumber(), lineCount, metrics);
|
||||
}
|
||||
|
||||
return EMPTY_LINE_NUMBER_SPACER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update to the current Decompiler display options
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
// ignoring the deprecated call for toolkit
|
||||
private void updateOptions() {
|
||||
syntax_color[ClangToken.KEYWORD_COLOR] = options.getKeywordColor();
|
||||
syntax_color[ClangToken.TYPE_COLOR] = options.getTypeColor();
|
||||
syntax_color[ClangToken.FUNCTION_COLOR] = options.getFunctionColor();
|
||||
syntax_color[ClangToken.COMMENT_COLOR] = options.getCommentColor();
|
||||
syntax_color[ClangToken.VARIABLE_COLOR] = options.getVariableColor();
|
||||
syntax_color[ClangToken.CONST_COLOR] = options.getConstantColor();
|
||||
syntax_color[ClangToken.PARAMETER_COLOR] = options.getParameterColor();
|
||||
syntax_color[ClangToken.GLOBAL_COLOR] = options.getGlobalColor();
|
||||
syntax_color[ClangToken.DEFAULT_COLOR] = options.getDefaultColor();
|
||||
syntax_color[ClangToken.ERROR_COLOR] = options.getErrorColor();
|
||||
syntaxColor[ClangToken.KEYWORD_COLOR] = options.getKeywordColor();
|
||||
syntaxColor[ClangToken.TYPE_COLOR] = options.getTypeColor();
|
||||
syntaxColor[ClangToken.FUNCTION_COLOR] = options.getFunctionColor();
|
||||
syntaxColor[ClangToken.COMMENT_COLOR] = options.getCommentColor();
|
||||
syntaxColor[ClangToken.VARIABLE_COLOR] = options.getVariableColor();
|
||||
syntaxColor[ClangToken.CONST_COLOR] = options.getConstantColor();
|
||||
syntaxColor[ClangToken.PARAMETER_COLOR] = options.getParameterColor();
|
||||
syntaxColor[ClangToken.GLOBAL_COLOR] = options.getGlobalColor();
|
||||
syntaxColor[ClangToken.DEFAULT_COLOR] = options.getDefaultColor();
|
||||
syntaxColor[ClangToken.ERROR_COLOR] = options.getErrorColor();
|
||||
|
||||
// setting the metrics here will indirectly trigger the new font to be used deeper in
|
||||
// the bowels of the FieldPanel (you can get the font from the metrics)
|
||||
|
@ -247,7 +221,6 @@ public class ClangLayoutController implements LayoutModel, LayoutModelListener {
|
|||
metrics = Toolkit.getDefaultToolkit().getFontMetrics(font);
|
||||
indentWidth = metrics.stringWidth(PrettyPrinter.INDENT_STRING);
|
||||
maxWidth = indentWidth * options.getMaxWidth();
|
||||
lineNumberFieldWidth = 0;
|
||||
|
||||
showLineNumbers = options.isDisplayLineNumbers();
|
||||
}
|
||||
|
@ -264,11 +237,6 @@ public class ClangLayoutController implements LayoutModel, LayoutModelListener {
|
|||
fieldList = new Field[lineCount]; // One field for each "C" line
|
||||
numIndexes = BigInteger.valueOf(lineCount);
|
||||
|
||||
lineNumberFieldWidth = 0;
|
||||
if (showLineNumbers && !isError) {
|
||||
lineNumberFieldWidth = LineNumberFieldElement.getFieldWidth(metrics, lineCount);
|
||||
}
|
||||
|
||||
for (int i = 0; i < lineCount; ++i) {
|
||||
ClangLine oneLine = lines.get(i);
|
||||
fieldList[i] = createTextFieldForLine(oneLine, lineCount, showLineNumbers);
|
||||
|
@ -600,81 +568,6 @@ public class ClangLayoutController implements LayoutModel, LayoutModelListener {
|
|||
// Inner Classes
|
||||
//==================================================================================================
|
||||
|
||||
private static class LineNumberFieldElement extends ClangFieldElement {
|
||||
private static final Color FOREGROUND_COLOR = new Color(125, 125, 125);
|
||||
private int uniformWidth;
|
||||
|
||||
private LineNumberFieldElement(int lineNumber, int lineCount, FontMetrics fontMetrics) {
|
||||
super(ClangToken.buildSpacer(null, 0, ""), createAttributedLineNumberString(lineNumber,
|
||||
lineCount, FOREGROUND_COLOR, fontMetrics), 0);
|
||||
uniformWidth = calculateUniformStringWidth(fontMetrics);
|
||||
}
|
||||
|
||||
private static String createLineNumberString(int lineNumber, int lineCount) {
|
||||
|
||||
String lineCountString = Integer.toString(lineCount);
|
||||
int maxNumberOfDigits = lineCountString.length();
|
||||
|
||||
String lineNumberString = Integer.toString(lineNumber);
|
||||
int lineNumberLength = lineNumberString.length();
|
||||
int padLength = maxNumberOfDigits - lineNumberLength;
|
||||
|
||||
StringBuffer buffy = new StringBuffer();
|
||||
for (int i = 0; i < padLength; i++) {
|
||||
buffy.append(' ');
|
||||
}
|
||||
buffy.append(lineNumberString).append(' '); // space for separation
|
||||
return buffy.toString();
|
||||
}
|
||||
|
||||
private static AttributedString createAttributedLineNumberString(int lineNumber,
|
||||
int lineCount, Color foregroundColor, FontMetrics fontMetrics) {
|
||||
return new AttributedString(createLineNumberString(lineNumber, lineCount),
|
||||
foregroundColor, fontMetrics);
|
||||
}
|
||||
|
||||
static int getFieldWidth(FontMetrics fontMetrics, int lineCnt) {
|
||||
int largestCharacterWidth = getLargestCharacterWidth(fontMetrics);
|
||||
int numberOfCharacters = createLineNumberString(0, lineCnt).length();
|
||||
return numberOfCharacters * largestCharacterWidth;
|
||||
}
|
||||
|
||||
private int calculateUniformStringWidth(FontMetrics fontMetrics) {
|
||||
int largestCharacterWidth = getLargestCharacterWidth(fontMetrics);
|
||||
int numberOfCharacters = getText().length();
|
||||
return numberOfCharacters * largestCharacterWidth;
|
||||
}
|
||||
|
||||
private static int getLargestCharacterWidth(FontMetrics fontMetrics) {
|
||||
// use the biggest number char (since that's what we paint in this object)
|
||||
// for determining the a space to use as a guide
|
||||
return fontMetrics.stringWidth("9");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paint(JComponent c, Graphics g, int x, int y) {
|
||||
// paint our text
|
||||
super.paint(c, g, 0, 0);
|
||||
|
||||
// paint a vertical rule
|
||||
Color color = getColor(0);
|
||||
g.setColor(color);
|
||||
|
||||
FontMetrics fontMetrics = g.getFontMetrics();
|
||||
int topX = fontMetrics.getMaxAscent() + 1; // fudge for font painting differences
|
||||
int maxDescent = fontMetrics.getMaxDescent();
|
||||
|
||||
int baselineX = maxDescent + 1; // fudge for font painting differences
|
||||
g.drawLine(uniformWidth, -topX, uniformWidth, baselineX);
|
||||
}
|
||||
|
||||
@Override
|
||||
// overridden so that our width reflects our custom width
|
||||
public int getStringWidth() {
|
||||
return uniformWidth + 3; // fudge for keeping the c code off the line number bar
|
||||
}
|
||||
}
|
||||
|
||||
private class FieldNumberColumnPair {
|
||||
private final int fieldNumber;
|
||||
private final int column;
|
||||
|
|
|
@ -15,68 +15,32 @@
|
|||
*/
|
||||
package ghidra.app.decompiler.component;
|
||||
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Rectangle;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.JComponent;
|
||||
|
||||
import docking.widgets.fieldpanel.field.*;
|
||||
import docking.widgets.fieldpanel.internal.FieldBackgroundColorManager;
|
||||
import docking.widgets.fieldpanel.internal.PaintContext;
|
||||
import docking.widgets.fieldpanel.support.*;
|
||||
import docking.widgets.fieldpanel.support.FieldLocation;
|
||||
import docking.widgets.fieldpanel.support.HighlightFactory;
|
||||
import ghidra.app.decompiler.ClangToken;
|
||||
|
||||
public class ClangTextField extends WrappingVerticalLayoutTextField {
|
||||
|
||||
private List<ClangToken> tokenList;
|
||||
private FieldElement lineNumberFieldElement;
|
||||
private final int lineNumber;
|
||||
|
||||
private static FieldElement createSingleLineElement(FieldElement[] textElements) {
|
||||
return new CompositeFieldElement(textElements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the offset of the x position for the given line number element. The line
|
||||
* numbers of the decompiler appear to the left of the data and thus offset the actual data
|
||||
* by the width of the line numbers. The line numbers may be disabled, in which case the
|
||||
* given FieldElement will have no width.
|
||||
*
|
||||
* @param initialX The original x value passed to the constructor of this class
|
||||
* @param lineNumberElement he line number element for this field from which we get a width
|
||||
* @return the calculated offset
|
||||
*/
|
||||
private static int calculateXPositionWithLineNumberOffset(int initialX,
|
||||
FieldElement lineNumberElement) {
|
||||
return initialX + lineNumberElement.getStringWidth();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the modified width for this field. This is a factor of line numbers and any
|
||||
* x offset given to this field element.
|
||||
*
|
||||
* @param initialX The original x value passed to the constructor of this class
|
||||
* @param lineNumberElement The line number element for this field from which we get a width
|
||||
* @param initialWidth The initial width we are allowed to take up
|
||||
* @return the modified width for this field. This is a factor of line numbers and any
|
||||
* x offset given to this field element.
|
||||
*/
|
||||
private static int calculateWidthFromXPosition(int initialX, FieldElement lineNumberElement,
|
||||
int initialWidth) {
|
||||
return initialWidth - calculateXPositionWithLineNumberOffset(initialX, lineNumberElement);
|
||||
}
|
||||
|
||||
public ClangTextField(List<ClangToken> tokenList, FieldElement[] fieldElements,
|
||||
FieldElement lineNumberFieldElement, int x, int width, HighlightFactory hlFactory) {
|
||||
super(createSingleLineElement(fieldElements),
|
||||
calculateXPositionWithLineNumberOffset(x, lineNumberFieldElement),
|
||||
calculateWidthFromXPosition(x, lineNumberFieldElement, width), 30, hlFactory, false);
|
||||
public ClangTextField(List<ClangToken> tokenList, FieldElement[] fieldElements, int x,
|
||||
int lineNumber, int width, HighlightFactory hlFactory) {
|
||||
super(createSingleLineElement(fieldElements), x, width - x, 30, hlFactory, false);
|
||||
this.tokenList = tokenList;
|
||||
this.lineNumberFieldElement = lineNumberFieldElement;
|
||||
this.lineNumber = lineNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the C language token at the indicated location.
|
||||
*
|
||||
* @param loc the field location
|
||||
* @return the token
|
||||
*/
|
||||
|
@ -96,11 +60,11 @@ public class ClangTextField extends WrappingVerticalLayoutTextField {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the token that is completely after the token that contains the given column
|
||||
* location. In this case, 'contains' means any position <b>inside</b> of a token, but
|
||||
* not at the beginning. So, if the column location is in the middle of a
|
||||
* token, it will return the index of next token. But if the column location is at
|
||||
* the beginning (just before the start) of a token, it will return the index of that token.
|
||||
* Returns the token that is completely after the token that contains the given column location.
|
||||
* In this case, 'contains' means any position <b>inside</b> of a token, but not at the
|
||||
* beginning. So, if the column location is in the middle of a token, it will return the index
|
||||
* of next token. But if the column location is at the beginning (just before the start) of a
|
||||
* token, it will return the index of that token.
|
||||
*
|
||||
* @param location containing the column at which to beginning searching
|
||||
* @return the next token starting after the given column
|
||||
|
@ -156,51 +120,7 @@ public class ClangTextField extends WrappingVerticalLayoutTextField {
|
|||
return tokenList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paint(JComponent c, Graphics g, PaintContext context, Rectangle clip,
|
||||
FieldBackgroundColorManager selectionMap, RowColLocation cursorLoc, int rowHeight) {
|
||||
|
||||
// Don't print line numbers; don't copy line numbers. We are assuming that the user only
|
||||
// wants to copy code.
|
||||
if (context.isPrinting() || context.isTextCopying()) {
|
||||
printTextWithoutLineNumbers(c, g, context, clip, selectionMap, cursorLoc, rowHeight);
|
||||
return;
|
||||
}
|
||||
|
||||
// paint our line number
|
||||
lineNumberFieldElement.paint(c, g, 0, 0);
|
||||
super.paint(c, g, context, clip, selectionMap, cursorLoc, rowHeight);
|
||||
}
|
||||
|
||||
private void printTextWithoutLineNumbers(JComponent c, Graphics g, PaintContext context,
|
||||
Rectangle clip, FieldBackgroundColorManager selectionMap, RowColLocation cursorLoc,
|
||||
int rowHeight) {
|
||||
int oringalStartX = startX;
|
||||
try {
|
||||
// strip off the line number padding...
|
||||
stripLineNumbersAndLayoutText();
|
||||
super.paint(c, g, context, clip, selectionMap, cursorLoc, rowHeight);
|
||||
}
|
||||
finally {
|
||||
// ...restore the line number padding
|
||||
reapplyLineNumbersAndLayoutText(oringalStartX);
|
||||
}
|
||||
}
|
||||
|
||||
private void stripLineNumbersAndLayoutText() {
|
||||
startX = startX - lineNumberFieldElement.getStringWidth();
|
||||
}
|
||||
|
||||
private void reapplyLineNumbersAndLayoutText(int originalStartX) {
|
||||
startX = originalStartX;
|
||||
}
|
||||
|
||||
public int getLineNumberWidth() {
|
||||
return lineNumberFieldElement.getStringWidth();
|
||||
}
|
||||
|
||||
public int getLineNumber() {
|
||||
String text = lineNumberFieldElement.getText().trim();
|
||||
return Integer.parseInt(text);
|
||||
return lineNumber;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,5 +46,5 @@ public interface DecompilerCallbackHandler {
|
|||
|
||||
void goToFunction(Function function, boolean newWindow);
|
||||
|
||||
void doWheNotBusy(Callback c);
|
||||
void doWhenNotBusy(Callback c);
|
||||
}
|
||||
|
|
|
@ -33,7 +33,8 @@ import ghidra.util.bean.field.AnnotatedTextFieldElement;
|
|||
import utility.function.Callback;
|
||||
|
||||
/**
|
||||
* Coordinates the interactions between the DecompilerProvider, DecompilerPanel, and the DecompilerManager
|
||||
* Coordinates the interactions between the DecompilerProvider, DecompilerPanel, and the
|
||||
* DecompilerManager
|
||||
*/
|
||||
|
||||
public class DecompilerController {
|
||||
|
@ -55,7 +56,6 @@ public class DecompilerController {
|
|||
new DecompilerPanel(this, options, clipboard, decompilerMgr.getTaskMonitorComponent());
|
||||
|
||||
decompilerPanel.setHoverMode(true);
|
||||
|
||||
}
|
||||
|
||||
public DecompilerPanel getDecompilerPanel() {
|
||||
|
@ -67,8 +67,8 @@ public class DecompilerController {
|
|||
//==================================================================================================
|
||||
|
||||
/**
|
||||
* Called by the provider when the provider is disposed. Once dispose is called, it should
|
||||
* never be used again.
|
||||
* Called by the provider when the provider is disposed. Once dispose is called, it should never
|
||||
* be used again.
|
||||
*/
|
||||
public void dispose() {
|
||||
clearCache();
|
||||
|
@ -77,8 +77,8 @@ public class DecompilerController {
|
|||
}
|
||||
|
||||
/**
|
||||
* clears all internal state and releases all resources. Called when the provider is no
|
||||
* longer visible or the currently displayed program is closed.
|
||||
* clears all internal state and releases all resources. Called when the provider is no longer
|
||||
* visible or the currently displayed program is closed.
|
||||
*/
|
||||
public void clear() {
|
||||
currentSelection = null;
|
||||
|
@ -93,8 +93,8 @@ public class DecompilerController {
|
|||
* re-decompile use {@link #refreshDisplay(Program, ProgramLocation, File)}.
|
||||
*
|
||||
* @param program the program for the given location
|
||||
* @param location the location containing the function to be displayed and the location in
|
||||
* that function to position the cursor.
|
||||
* @param location the location containing the function to be displayed and the location in that
|
||||
* function to position the cursor.
|
||||
* @param viewerPosition the viewer position
|
||||
*/
|
||||
public void display(Program program, ProgramLocation location, ViewerPosition viewerPosition) {
|
||||
|
@ -138,6 +138,7 @@ public class DecompilerController {
|
|||
|
||||
/**
|
||||
* Sets new decompiler options and triggers a new decompile.
|
||||
*
|
||||
* @param decompilerOptions the options
|
||||
*/
|
||||
public void setOptions(DecompileOptions decompilerOptions) {
|
||||
|
@ -159,8 +160,8 @@ public class DecompilerController {
|
|||
}
|
||||
|
||||
/**
|
||||
* Resets the native decompiler process. Call this method when the decompiler's view
|
||||
* of a program has been invalidated, such as when a new overlay space has been added.
|
||||
* Resets the native decompiler process. Call this method when the decompiler's view of a
|
||||
* program has been invalidated, such as when a new overlay space has been added.
|
||||
*/
|
||||
public void resetDecompiler() {
|
||||
decompilerMgr.resetDecompiler();
|
||||
|
@ -172,6 +173,7 @@ public class DecompilerController {
|
|||
|
||||
/**
|
||||
* Called by the DecompilerManager to update the currently displayed DecompileData
|
||||
*
|
||||
* @param decompileData the new data
|
||||
*/
|
||||
public void setDecompileData(DecompileData decompileData) {
|
||||
|
@ -199,15 +201,16 @@ public class DecompilerController {
|
|||
//==================================================================================================
|
||||
|
||||
public void doWhenNotBusy(Callback c) {
|
||||
callbackHandler.doWheNotBusy(c);
|
||||
callbackHandler.doWhenNotBusy(c);
|
||||
}
|
||||
|
||||
/**
|
||||
* Always decompiles the function containing the given location before positioning the
|
||||
* decompilerPanel's cursor to the closest equivalent position.
|
||||
*
|
||||
* @param program the program for the given location
|
||||
* @param location the location containing the function to be displayed and the location in
|
||||
* that function to position the cursor.
|
||||
* @param location the location containing the function to be displayed and the location in that
|
||||
* function to position the cursor.
|
||||
* @param debugFile the debug file
|
||||
*/
|
||||
public void refreshDisplay(Program program, ProgramLocation location, File debugFile) {
|
||||
|
|
|
@ -39,8 +39,10 @@ import docking.widgets.fieldpanel.support.*;
|
|||
import docking.widgets.indexedscrollpane.IndexedScrollPane;
|
||||
import ghidra.app.decompiler.*;
|
||||
import ghidra.app.decompiler.component.hover.DecompilerHoverService;
|
||||
import ghidra.app.decompiler.component.margin.*;
|
||||
import ghidra.app.plugin.core.decompile.DecompilerClipboardProvider;
|
||||
import ghidra.app.plugin.core.decompile.actions.FieldBasedSearchLocation;
|
||||
import ghidra.app.util.viewer.util.ScrollpaneAlignedHorizontalLayout;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.Program;
|
||||
|
@ -55,7 +57,7 @@ import ghidra.util.task.SwingUpdateManager;
|
|||
* Class to handle the display of a decompiled function
|
||||
*/
|
||||
public class DecompilerPanel extends JPanel implements FieldMouseListener, FieldLocationListener,
|
||||
FieldSelectionListener, ClangHighlightListener {
|
||||
FieldSelectionListener, ClangHighlightListener, LayoutListener {
|
||||
|
||||
private final static Color NON_FUNCTION_BACKGROUND_COLOR_DEF = new Color(220, 220, 220);
|
||||
|
||||
|
@ -64,9 +66,15 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
|||
|
||||
private final DecompilerController controller;
|
||||
private final DecompileOptions options;
|
||||
private LineNumberDecompilerMarginProvider lineNumbersMargin;
|
||||
|
||||
private DecompilerFieldPanel fieldPanel;
|
||||
private final DecompilerFieldPanel fieldPanel;
|
||||
private ClangLayoutController layoutMgr;
|
||||
private final IndexedScrollPane scroller;
|
||||
private final JComponent taskMonitorComponent;
|
||||
|
||||
private final List<DecompilerMarginProvider> marginProviders = new ArrayList<>();
|
||||
private final VerticalLayoutPixelIndexMap pixmap = new VerticalLayoutPixelIndexMap();
|
||||
|
||||
private HighlightFactory hlFactory;
|
||||
private ClangHighlightController highlightController;
|
||||
|
@ -99,6 +107,7 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
|||
this.controller = controller;
|
||||
this.options = options;
|
||||
this.clipboard = clipboard;
|
||||
this.taskMonitorComponent = taskMonitorComponent;
|
||||
FontMetrics metrics = getFontMetrics(options);
|
||||
if (clipboard != null) {
|
||||
clipboard.setFontMetrics(metrics);
|
||||
|
@ -109,10 +118,11 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
|||
fieldPanel = new DecompilerFieldPanel(layoutMgr);
|
||||
setBackground(options.getCodeViewerBackgroundColor());
|
||||
|
||||
IndexedScrollPane scroller = new IndexedScrollPane(fieldPanel);
|
||||
scroller = new IndexedScrollPane(fieldPanel);
|
||||
fieldPanel.addFieldSelectionListener(this);
|
||||
fieldPanel.addFieldMouseListener(this);
|
||||
fieldPanel.addFieldLocationListener(this);
|
||||
fieldPanel.addLayoutListener(this);
|
||||
|
||||
decompilerHoverProvider = new DecompilerHoverProvider();
|
||||
|
||||
|
@ -127,6 +137,10 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
|||
|
||||
setPreferredSize(new Dimension(600, 400));
|
||||
setDecompileData(new EmptyDecompileData("No Function"));
|
||||
|
||||
if (options.isDisplayLineNumbers()) {
|
||||
addMarginProvider(lineNumbersMargin = new LineNumberDecompilerMarginProvider());
|
||||
}
|
||||
}
|
||||
|
||||
public List<ClangLine> getLines() {
|
||||
|
@ -905,6 +919,14 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void layoutsChanged(List<AnchoredLayout> layouts) {
|
||||
pixmap.layoutsChanged(layouts);
|
||||
for (DecompilerMarginProvider element : marginProviders) {
|
||||
element.setProgram(getProgram(), layoutMgr, pixmap);
|
||||
}
|
||||
}
|
||||
|
||||
private ProgramLocation getProgramLocation(Field field, FieldLocation location) {
|
||||
if (!(field instanceof ClangTextField)) {
|
||||
return null;
|
||||
|
@ -1131,6 +1153,49 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
|||
searchHighlightColor = decompilerOptions.getSearchHighlightColor();
|
||||
|
||||
highlightController.setHighlightColor(currentVariableHighlightColor);
|
||||
|
||||
if (options.isDisplayLineNumbers()) {
|
||||
if (lineNumbersMargin == null) {
|
||||
addMarginProvider(lineNumbersMargin = new LineNumberDecompilerMarginProvider());
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (lineNumbersMargin != null) {
|
||||
removeMarginProvider(lineNumbersMargin);
|
||||
lineNumbersMargin = null;
|
||||
}
|
||||
}
|
||||
|
||||
for (DecompilerMarginProvider element : marginProviders) {
|
||||
element.setOptions(options);
|
||||
}
|
||||
}
|
||||
|
||||
public void addMarginProvider(DecompilerMarginProvider provider) {
|
||||
marginProviders.add(provider);
|
||||
provider.setOptions(options);
|
||||
provider.setProgram(getProgram(), layoutMgr, pixmap);
|
||||
buildPanels();
|
||||
}
|
||||
|
||||
public void removeMarginProvider(DecompilerMarginProvider provider) {
|
||||
marginProviders.remove(provider);
|
||||
buildPanels();
|
||||
}
|
||||
|
||||
private void buildPanels() {
|
||||
removeAll();
|
||||
add(buildLeftComponent(), BorderLayout.WEST);
|
||||
add(scroller, BorderLayout.CENTER);
|
||||
add(taskMonitorComponent, BorderLayout.SOUTH);
|
||||
}
|
||||
|
||||
private JComponent buildLeftComponent() {
|
||||
JPanel leftPanel = new JPanel(new ScrollpaneAlignedHorizontalLayout(scroller));
|
||||
for (DecompilerMarginProvider marginProvider : marginProviders) {
|
||||
leftPanel.add(marginProvider.getComponent());
|
||||
}
|
||||
return leftPanel;
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.decompiler.component.margin;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.math.BigInteger;
|
||||
|
||||
import javax.swing.JComponent;
|
||||
|
||||
import docking.widgets.fieldpanel.LayoutModel;
|
||||
import ghidra.app.decompiler.DecompileOptions;
|
||||
import ghidra.app.decompiler.DecompilerMarginService;
|
||||
import ghidra.program.model.listing.Program;
|
||||
|
||||
/**
|
||||
* A provider of a margin Swing component
|
||||
*
|
||||
* <p>
|
||||
* To add a margin to the decompiler, a client must implement this interface to provide the
|
||||
* component that is actually added to the UI. For a reference implementation, see
|
||||
* {@link LineNumberDecompilerMarginProvider}.
|
||||
*/
|
||||
public interface DecompilerMarginProvider {
|
||||
|
||||
/**
|
||||
* Called whenever the program, function, or layout changes
|
||||
*
|
||||
* <p>
|
||||
* The implementation should keep a reference at least to the {@code model} and the
|
||||
* {@code pixmap} for later use during painting. The model provides access to the lines of
|
||||
* decompiler C code. Each layout corresponds to a single line of C code. For example, the first
|
||||
* line of code is rendered by the layout at index 0. The tenth is rendered by the layout at
|
||||
* index 9. Rarely, a line may be wrapped by the renderer, leading to a non-uniform layout. The
|
||||
* {@code pixmap} can map from a pixel's vertical position to the layout index at the same
|
||||
* position in the main panel. It accounts for scrolling an non-uniformity. It is safe to assume
|
||||
* the layouts render contiguous lines of C code. The recommended strategy for painting is thus:
|
||||
*
|
||||
* <ol>
|
||||
* <li>Compute the visible part of the margin needing repainting. See
|
||||
* {@link JComponent#getVisibleRect()}</li>
|
||||
* <li>Compute the layout indices for the vertical bounds of that part. See
|
||||
* {@link LayoutPixelIndexMap#getIndex(int)}</li>
|
||||
* <li>Iterate over the layouts within those bounds, inclusively.</li>
|
||||
* <li>Compute the vertical position of each layout and paint something appropriate for its
|
||||
* corresponding line. See {@link LayoutPixelIndexMap#getPixel(BigInteger)}</li>
|
||||
* </ol>
|
||||
*
|
||||
* <p>
|
||||
* A call to this method should cause the component to be repainted.
|
||||
*
|
||||
* @param program the program for the current function
|
||||
* @param model the line/token model
|
||||
* @param pixmap a map from pixels' y coordinates to layout index, i.e, line number
|
||||
*/
|
||||
void setProgram(Program program, LayoutModel model, LayoutPixelIndexMap pixmap);
|
||||
|
||||
/**
|
||||
* Get the Swing component implementing the actual margin, often {@code this}
|
||||
*
|
||||
* @return the component
|
||||
*/
|
||||
Component getComponent();
|
||||
|
||||
/**
|
||||
* Set the options for the margin
|
||||
*
|
||||
* <p>
|
||||
* This is called at least once when the provider is added to the margin service. See
|
||||
* {@link DecompilerMarginService#addMarginProvider(DecompilerMarginProvider)}. It subsequently
|
||||
* called whenever a decompiler option changes. To receive other options, the provider will need
|
||||
* to listen using its own mechanism.
|
||||
*
|
||||
* <p>
|
||||
* A call to this method should cause the component to be repainted. Implementors may choose to
|
||||
* repaint only when certain options change.
|
||||
*/
|
||||
default void setOptions(DecompileOptions options) {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.decompiler.component.margin;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
import docking.widgets.fieldpanel.LayoutModel;
|
||||
import ghidra.program.model.listing.Program;
|
||||
|
||||
/**
|
||||
* A mapping from pixel coordinate to layout index
|
||||
*
|
||||
* <p>
|
||||
* At the moment, the only implementation provides a map from vertical position to layout. While
|
||||
* this does not have to be the case, the documentation will presume the y coordinate.
|
||||
*/
|
||||
public interface LayoutPixelIndexMap {
|
||||
/**
|
||||
* Get the top of the layout with the given index
|
||||
*
|
||||
* <p>
|
||||
* Gets the minimum y coordinate of any pixel occupied by the layout having the given index. In
|
||||
* essence, this maps from layout index to vertical position, relative to the main panel's
|
||||
* viewport. This accounts for scrolling and non-uniform height among the layouts.
|
||||
*
|
||||
* @param index the index of the layout
|
||||
* @return the top of the layout, relative to the main panel's viewport
|
||||
*/
|
||||
int getPixel(BigInteger index);
|
||||
|
||||
/**
|
||||
* Get the index of the layout at the given position
|
||||
*
|
||||
* <p>
|
||||
* Get the index of the layout occupying the line of pixels in the main panel having the given y
|
||||
* coordinate. In essence, this maps from vertical position, relative to the main panel's
|
||||
* viewport, to layout index. This accounts for scrolling and non-uniform height among the
|
||||
* layouts.
|
||||
*
|
||||
* @implNote Clients should avoid frequent calls to this method. Even though it can be
|
||||
* implemented easily in log time, an invocation for every pixel or line of pixels
|
||||
* painted could still be unnecessarily expensive. It should only be necessary to call
|
||||
* this once or twice per repaint. See
|
||||
* {@link DecompilerMarginProvider#setProgram(Program, LayoutModel, LayoutPixelIndexMap)}.
|
||||
*
|
||||
* @param pixel the vertical position of the pixel, relative to the main panel's viewport
|
||||
* @return the index of the layout
|
||||
*/
|
||||
BigInteger getIndex(int pixel);
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.decompiler.component.margin;
|
||||
|
||||
import java.awt.*;
|
||||
import java.math.BigInteger;
|
||||
|
||||
import javax.swing.JPanel;
|
||||
|
||||
import docking.util.GraphicsUtils;
|
||||
import docking.widgets.fieldpanel.LayoutModel;
|
||||
import docking.widgets.fieldpanel.listener.IndexMapper;
|
||||
import docking.widgets.fieldpanel.listener.LayoutModelListener;
|
||||
import ghidra.app.decompiler.DecompileOptions;
|
||||
import ghidra.program.model.listing.Program;
|
||||
|
||||
/**
|
||||
* The built-in provider for the Decompiler's line number margin
|
||||
*/
|
||||
public class LineNumberDecompilerMarginProvider extends JPanel
|
||||
implements DecompilerMarginProvider, LayoutModelListener {
|
||||
|
||||
private LayoutPixelIndexMap pixmap;
|
||||
private LayoutModel model;
|
||||
|
||||
@Override
|
||||
public void setProgram(Program program, LayoutModel model, LayoutPixelIndexMap pixmap) {
|
||||
setLayoutManager(model);
|
||||
this.pixmap = pixmap;
|
||||
repaint();
|
||||
}
|
||||
|
||||
private void setLayoutManager(LayoutModel model) {
|
||||
if (this.model == model) {
|
||||
return;
|
||||
}
|
||||
if (this.model != null) {
|
||||
this.model.removeLayoutModelListener(this);
|
||||
}
|
||||
this.model = model;
|
||||
this.model.addLayoutModelListener(this);
|
||||
setWidthForLastLine();
|
||||
if (this.model != null) {
|
||||
this.model.addLayoutModelListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOptions(DecompileOptions options) {
|
||||
this.setFont(options.getDefaultFont());
|
||||
setWidthForLastLine();
|
||||
repaint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getComponent() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void modelSizeChanged(IndexMapper indexMapper) {
|
||||
setWidthForLastLine();
|
||||
repaint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dataChanged(BigInteger start, BigInteger end) {
|
||||
repaint();
|
||||
}
|
||||
|
||||
private void setWidthForLastLine() {
|
||||
if (model == null) {
|
||||
return;
|
||||
}
|
||||
int lastLine = model.getNumIndexes().intValueExact();
|
||||
int width = getFontMetrics(getFont()).stringWidth(Integer.toString(lastLine));
|
||||
setPreferredSize(new Dimension(width, 0));
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paint(Graphics g) {
|
||||
super.paint(g);
|
||||
Rectangle visible = getVisibleRect();
|
||||
BigInteger startIdx = pixmap.getIndex(visible.y);
|
||||
BigInteger endIdx = pixmap.getIndex(visible.y + visible.height);
|
||||
int ascent = g.getFontMetrics().getMaxAscent();
|
||||
for (BigInteger i = startIdx; i.compareTo(endIdx) <= 0; i = i.add(BigInteger.ONE)) {
|
||||
GraphicsUtils.drawString(this, g, i.add(BigInteger.ONE).toString(), 0,
|
||||
pixmap.getPixel(i) + ascent);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.decompiler.component.margin;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import docking.widgets.fieldpanel.support.AnchoredLayout;
|
||||
|
||||
/**
|
||||
* An implementation of {@link LayoutPixelIndexMap} for vertical coordinates
|
||||
*
|
||||
* <p>
|
||||
* This class implements {@link #getIndex(int)} in log time and {@link #getPixel(BigInteger)} in
|
||||
* constant time.
|
||||
*/
|
||||
public class VerticalLayoutPixelIndexMap implements LayoutPixelIndexMap {
|
||||
private BigInteger base = BigInteger.ZERO;
|
||||
private int[] yPositions = new int[0];
|
||||
private int size;
|
||||
|
||||
@Override
|
||||
public int getPixel(BigInteger index) {
|
||||
return yPositions[index.subtract(base).intValueExact()];
|
||||
}
|
||||
|
||||
protected int computeOff(int pixel) {
|
||||
int result = Arrays.binarySearch(yPositions, 0, size, pixel);
|
||||
if (result >= 0) {
|
||||
return result;
|
||||
}
|
||||
// result = -insertionPoint - 1, first index where acc[index] > pixel
|
||||
// want index = insertionPoint - 1, index where acc[index] < pixel < acc[index+1]
|
||||
// insertionPoint = -result - 1
|
||||
// index = -result - 2;
|
||||
return -result - 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigInteger getIndex(int pixel) {
|
||||
return base.add(BigInteger.valueOf(computeOff(pixel)));
|
||||
}
|
||||
|
||||
public void layoutsChanged(List<AnchoredLayout> layouts) {
|
||||
size = layouts.size();
|
||||
if (yPositions.length < size) {
|
||||
yPositions = new int[size];
|
||||
}
|
||||
int i = 0;
|
||||
base = layouts.isEmpty() ? BigInteger.ZERO : layouts.get(0).getIndex();
|
||||
for (AnchoredLayout l : layouts) {
|
||||
assert l.getIndex().subtract(base).intValueExact() == i;
|
||||
yPositions[i] = l.getYPos();
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,8 +20,7 @@ import java.util.*;
|
|||
import org.jdom.Element;
|
||||
|
||||
import ghidra.app.CorePluginPackage;
|
||||
import ghidra.app.decompiler.ClangToken;
|
||||
import ghidra.app.decompiler.DecompilerHighlightService;
|
||||
import ghidra.app.decompiler.*;
|
||||
import ghidra.app.decompiler.component.hover.DecompilerHoverService;
|
||||
import ghidra.app.events.*;
|
||||
import ghidra.app.plugin.PluginCategoryNames;
|
||||
|
@ -40,7 +39,6 @@ import ghidra.util.task.SwingUpdateManager;
|
|||
/**
|
||||
* Plugin for producing a high-level C interpretation of assembly functions.
|
||||
*/
|
||||
//@formatter:off
|
||||
@PluginInfo(
|
||||
status = PluginStatus.RELEASED,
|
||||
packageName = CorePluginPackage.NAME,
|
||||
|
@ -56,9 +54,7 @@ import ghidra.util.task.SwingUpdateManager;
|
|||
ProgramActivatedPluginEvent.class, ProgramOpenedPluginEvent.class,
|
||||
ProgramLocationPluginEvent.class, ProgramSelectionPluginEvent.class,
|
||||
ProgramClosedPluginEvent.class
|
||||
}
|
||||
)
|
||||
//@formatter:on
|
||||
})
|
||||
public class DecompilePlugin extends Plugin {
|
||||
|
||||
private PrimaryDecompilerProvider connectedProvider;
|
||||
|
@ -69,9 +65,8 @@ public class DecompilePlugin extends Plugin {
|
|||
private ProgramSelection currentSelection;
|
||||
|
||||
/**
|
||||
* Delay location changes to allow location events to settle down.
|
||||
* This happens when a readDataState occurs when a tool is restored
|
||||
* or when switching program tabs.
|
||||
* Delay location changes to allow location events to settle down. This happens when a
|
||||
* readDataState occurs when a tool is restored or when switching program tabs.
|
||||
*/
|
||||
SwingUpdateManager delayedLocationUpdateMgr = new SwingUpdateManager(200, 200, () -> {
|
||||
if (currentLocation == null) {
|
||||
|
@ -96,6 +91,8 @@ public class DecompilePlugin extends Plugin {
|
|||
|
||||
private void registerServices() {
|
||||
registerServiceProvided(DecompilerHighlightService.class, connectedProvider);
|
||||
// Allow pluggable margin providers for disconnected providers?
|
||||
registerServiceProvided(DecompilerMarginService.class, connectedProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -209,7 +209,7 @@ public class DecompilerClipboardProvider extends ByteCopier
|
|||
LayoutModel model = provider.getDecompilerPanel().getLayoutModel();
|
||||
Layout layout = model.getLayout(BigInteger.valueOf(lineNumber));
|
||||
ClangTextField field = (ClangTextField) layout.getField(0);
|
||||
int numSpaces = (field.getStartX() - field.getLineNumberWidth()) / spaceCharWidthInPixels;
|
||||
int numSpaces = field.getStartX() / spaceCharWidthInPixels;
|
||||
for (int i = 0; i < numSpaces; i++) {
|
||||
buffer.append(' ');
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ import docking.widgets.fieldpanel.support.ViewerPosition;
|
|||
import ghidra.GhidraOptions;
|
||||
import ghidra.app.decompiler.*;
|
||||
import ghidra.app.decompiler.component.*;
|
||||
import ghidra.app.decompiler.component.margin.DecompilerMarginProvider;
|
||||
import ghidra.app.nav.*;
|
||||
import ghidra.app.plugin.core.decompile.actions.*;
|
||||
import ghidra.app.services.*;
|
||||
|
@ -55,7 +56,7 @@ import utility.function.Callback;
|
|||
|
||||
public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
||||
implements DomainObjectListener, OptionsChangeListener, DecompilerCallbackHandler,
|
||||
DecompilerHighlightService {
|
||||
DecompilerHighlightService, DecompilerMarginService {
|
||||
|
||||
private static final String OPTIONS_TITLE = "Decompiler";
|
||||
|
||||
|
@ -385,6 +386,7 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
|||
|
||||
/**
|
||||
* Sets the current program and adds/removes itself as a domainObjectListener
|
||||
*
|
||||
* @param newProgram the new program or null to clear out the current program.
|
||||
*/
|
||||
void doSetProgram(Program newProgram) {
|
||||
|
@ -422,9 +424,10 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
|||
}
|
||||
|
||||
/**
|
||||
* sets the current location for this provider. If the provider is not visible, it does not
|
||||
* pass it on to the controller. When the component is later shown, the current location
|
||||
* will then be passed to the controller.
|
||||
* sets the current location for this provider. If the provider is not visible, it does not pass
|
||||
* it on to the controller. When the component is later shown, the current location will then be
|
||||
* passed to the controller.
|
||||
*
|
||||
* @param loc the location to compile and set the cursor.
|
||||
* @param viewerPosition if non-null the position at which to scroll the view.
|
||||
*/
|
||||
|
@ -471,8 +474,8 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns a string that shows the current line with the field under the cursor in between
|
||||
* '[]' chars.
|
||||
* Returns a string that shows the current line with the field under the cursor in between '[]'
|
||||
* chars.
|
||||
*
|
||||
* @return the string
|
||||
*/
|
||||
|
@ -648,7 +651,7 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
|||
}
|
||||
|
||||
@Override
|
||||
public void doWheNotBusy(Callback c) {
|
||||
public void doWhenNotBusy(Callback c) {
|
||||
followUpWork.offer(c);
|
||||
followUpWorkUpdater.update();
|
||||
}
|
||||
|
@ -678,7 +681,7 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
|||
|
||||
// transfer any state after the new decompiler is initialized
|
||||
DecompilerPanel newPanel = newProvider.getDecompilerPanel();
|
||||
newProvider.doWheNotBusy(() -> {
|
||||
newProvider.doWhenNotBusy(() -> {
|
||||
newPanel.setViewerPosition(myViewPosition);
|
||||
newPanel.cloneHighlights(myPanel);
|
||||
});
|
||||
|
@ -1113,4 +1116,14 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
|||
void handleTokenRenamed(ClangToken tokenAtCursor, String newName) {
|
||||
controller.getDecompilerPanel().tokenRenamed(tokenAtCursor, newName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addMarginProvider(DecompilerMarginProvider provider) {
|
||||
getDecompilerPanel().addMarginProvider(provider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeMarginProvider(DecompilerMarginProvider provider) {
|
||||
getDecompilerPanel().removeMarginProvider(provider);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -310,7 +310,7 @@ public class ConvertConstantTask implements Callback {
|
|||
catch (InterruptedException e) {
|
||||
return;
|
||||
}
|
||||
context.getComponentProvider().doWheNotBusy(this);
|
||||
context.getComponentProvider().doWhenNotBusy(this);
|
||||
}
|
||||
else {
|
||||
applyPrimaryEquate();
|
||||
|
|
|
@ -25,7 +25,7 @@ import docking.widgets.fieldpanel.listener.LayoutModelListener;
|
|||
* using a BigFieldPanel.
|
||||
*/
|
||||
|
||||
public interface LayoutModel {
|
||||
public interface LayoutModel extends Iterable<Layout> {
|
||||
|
||||
/**
|
||||
* Returns true if every index returns a non-null layout and all the layouts
|
||||
|
@ -68,6 +68,7 @@ public interface LayoutModel {
|
|||
*
|
||||
* @return new iterator
|
||||
*/
|
||||
@Override
|
||||
public default LayoutModelIterator iterator() {
|
||||
return new LayoutModelIterator(this);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue