Merge remote-tracking branch

'origin/GT-3292_dragonmacher_PR-784_00rsiere_decompiler-multi-highlight'
(closes #784)

Conflicts:
	Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileOptions.java
This commit is contained in:
Ryan Kurtz 2019-12-13 11:35:09 -05:00
commit 59e88c6ca9
40 changed files with 2821 additions and 490 deletions

View file

@ -31,7 +31,6 @@ import docking.action.*;
import docking.widgets.OptionDialog;
import docking.widgets.combobox.GhidraComboBox;
import docking.widgets.dialogs.InputDialog;
import docking.widgets.dialogs.InputDialogListener;
import docking.widgets.filechooser.GhidraFileChooser;
import docking.widgets.filechooser.GhidraFileChooserMode;
import docking.widgets.pathmanager.PathnameTablePanel;
@ -446,8 +445,7 @@ class ParseDialog extends DialogComponentProvider {
private void saveAs(ComboBoxItem item) {
InputDialog d =
new InputDialog("Enter Profile Name", "Profile Name", (InputDialogListener) null);
InputDialog d = new InputDialog("Enter Profile Name", "Profile Name");
plugin.getTool().showDialog(d, getComponent());
String name = d.getValue();

View file

@ -15,8 +15,7 @@
*/
package ghidra.test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.*;
import java.util.ArrayList;
import java.util.List;
@ -78,7 +77,6 @@ public abstract class AbstractProgramBasedTest extends AbstractGhidraHeadedInteg
@After
public void tearDown() throws Exception {
env.release(program);
env.dispose();
}

View file

@ -6,7 +6,6 @@ Module.manifest||GHIDRA||||END|
build.gradle||GHIDRA||||END|
src/decompile/.cproject||GHIDRA||||END|
src/decompile/.project||GHIDRA||||END|
src/decompile/.settings/org.eclipse.jdt.core.prefs||GHIDRA||||END|
src/decompile/build.gradle||GHIDRA||||END|
src/decompile/cpp/.gitignore||GHIDRA||||END|
src/decompile/cpp/Doxyfile||GHIDRA|||Most of this file is autogenerated by doxygen which falls under the GPL - output from GPL products are NOT GPL! - mjbell4|END|

View file

@ -572,9 +572,9 @@
<P style="margin-left: 80px;">Ghidra will do stack analysis that will recover parameters and
return types, but for many programs, the analysis the decompiler does is better.</P>
<P style="margin-left: 80px;"><IMG alt="" src="../../shared/note.png" border="0">There is a
prototype plug-in that automatically pulls in the decompiler derived information and applies it
to each function as the function is created.</P>
<P style="margin-left: 80px;"><IMG alt="" src="../../shared/note.png" border="0">The
Decompiler Parameter ID analyzer automatically pulls in the decompiler derived
information and applies it to each function as the function is created.</P>
<P style="margin-left: 80px;"><IMG alt="" src="../../shared/note.png" border="0"> If a variable
displayed in the assembly window has an undefined type, the decompiler will still respect the
@ -597,11 +597,12 @@
on the stack, but for many programs, the analysis the decompiler does is better.<BR>
</P>
<P style="margin-left: 80px;"><IMG alt="" src="../../shared/note.png" border="0"> There is a
prototype plug-in that automatically pulls in the decompiler derived information and applies it
to each function as the function is created.&nbsp; The plugin by default will not commit local
variable definitions, either stack or register locals.&nbsp; Committing locals automatically
can be turned on by changing the analysis options for the Decompiler Parameter ID plugin.&nbsp;
<P style="margin-left: 80px;"><IMG alt="" src="../../shared/note.png" border="0">he
Decompiler Parameter ID analyzer automatically pulls in the decompiler derived
information and applies it to each function as the function is created. The analyzer by default
will not commit local variable definitions, either stack or register locals.
Committing locals automatically
can be turned on by changing the Decompiler Parameter ID analysis options.
In most cases it is better to commit locals only for certain functions that you really care
about, or after the data type definitions (structures, etc...) have settled down for the
program you are Reverse Engineering.<BR>
@ -720,6 +721,79 @@
contribute to the creation of the value in the variable under the cursor.</P>
</BLOCKQUOTE>
<BR>
<BR>
<BR>
<!-- Secondary Highlights -->
<BLOCKQUOTE>
<H2>Secondary Highlights</H2>
<BLOCKQUOTE>
<P>
Secondary highlights are highlights that you can apply to text that will
stay until you remove them (this is in contrast to normal highlights, which go
away whenever you click in the Decompiler). Further, each secondary highlight will get a
different color (chosen randomly or by the user). The Decompiler will remember
the color previously used for a particular highlighted piece of text <b>for a
given Ghidra session</b>. Use the actions listed below to add and remove
secondary highlights.
</P>
<H3><A name="Set_Secondary_Highlight"></A>Set Secondary Highlight</H3>
<BLOCKQUOTE>
<P>
Use this action to apply a secondary highlight to all text matching the text of the
token under the cursor. This action will reuse the previously applied color for
that text or choose a new color randomly. This action is accessible from the
context menu via
<b>Secondary Highlight<img border="0" src="../../shared/arrow.gif">Set Highlight</b>
</P>
</BLOCKQUOTE>
<H3><A name="Set_Secondary_Highlight_With_Color"></A>Set Secondary Highlight With Color</H3>
<BLOCKQUOTE>
<P>
Use this action to apply a secondary highlight to all text matching the text of the
token under the cursor. This action will show a Color Chooser dialog initialized with
the previously applied color for
that text or choose a new color randomly. This action is accessible from the
context menu via
<b>Secondary Highlight<img border="0" src="../../shared/arrow.gif">Set Highlight...</b>
</P>
</BLOCKQUOTE>
<H3><A name="Remove_Secondary_Highlight"></A>Remove Secondary Highlight</H3>
<BLOCKQUOTE>
<P>
Use this action to remove an existing secondary highlight under the cursor.
This action is accessible from the context menu via
<b>Secondary Highlight<img border="0" src="../../shared/arrow.gif">Remove Highlight</b>
</P>
</BLOCKQUOTE>
<H3><A name="Remove_All_Secondary_Highlights"></A>Remove All Secondary Highlights</H3>
<BLOCKQUOTE>
<P>
Use this action to remove all existing secondary highlights.
This action is accessible from the context menu via
<b>Secondary Highlight<img border="0" src="../../shared/arrow.gif">Remove All Highlights</b>
</P>
</BLOCKQUOTE>
</BLOCKQUOTE>
</BLOCKQUOTE>
<!-- End Secondary Highlights -->
<BR>
<BR>
<BR>
<H3><A name="Rename_Variable"></A>Rename Variable</H3>
<BLOCKQUOTE>

View file

@ -67,6 +67,10 @@ public class ClangLine {
return tokens.get(i);
}
public int indexOfToken(ClangToken token) {
return tokens.indexOf(token);
}
public String toDebugString(List<ClangToken> calloutTokens) {
return toDebugString(calloutTokens, "[", "]");

View file

@ -245,6 +245,9 @@ public class ClangToken implements ClangNode {
@Override
public String toString() {
if (lineparent != null) {
return lineparent.getLineNumber() + ": " + text;
}
return text;
}

View file

@ -29,6 +29,7 @@ import ghidra.program.model.listing.Program;
import ghidra.program.util.ProgramLocation;
import ghidra.program.util.ProgramSelection;
import ghidra.util.bean.field.AnnotatedTextFieldElement;
import utility.function.Callback;
public class CDisplayPanel extends JPanel implements DecompilerCallbackHandler {
@ -157,6 +158,11 @@ public class CDisplayPanel extends JPanel implements DecompilerCallbackHandler {
// stub
}
@Override
public void doWheNotBusy(Callback c) {
// stub
}
public void clearAndShowMessage(String message) {
controller.setDecompileData(new EmptyDecompileData(message));
paintImmediately(getBounds());

View file

@ -17,70 +17,62 @@ package ghidra.app.decompiler.component;
import java.awt.Color;
import java.util.*;
import java.util.function.Function;
import java.util.function.Supplier;
import docking.widgets.EventTrigger;
import docking.widgets.fieldpanel.field.Field;
import docking.widgets.fieldpanel.support.FieldLocation;
import ghidra.app.decompiler.*;
import ghidra.app.plugin.core.decompile.actions.TokenHighlightColorProvider;
import ghidra.program.model.pcode.PcodeOp;
import ghidra.program.model.pcode.Varnode;
import ghidra.util.ColorUtils;
import util.CollectionUtils;
/**
* Class to handle highlights for a decompiled function.
*
* <p>This class does not painting directly. Rather, this class tracks the currently highlighted
* tokens and then sets the highlight color on the token when it is highlighted and clears the
* highlight color when the highlight is removed.
*
* <p>This class maintains the notion of 'primary' highlights and 'secondary' highlights.
* Primary highlights are considered transient and get cleared whenever the location changes.
* Secondary highlights will stay until they are manually cleared by a user action. Primary
* highlights happen when the user clicks around the Decompiler. They show state such as the
* current field, impact of a variable (via a slicing action), or related syntax (such as
* matching braces). Secondary highlights are triggered by the user to show all occurrences of
* a particular variable. Further, the user can apply multiple secondary highlights at the
* same time, with different colors for each highlight.
*/
public abstract class ClangHighlightController {
public static Color DEFAULT_HIGHLIGHT_COLOR = new Color(255, 255, 0, 128);
public static ClangHighlightController dummyIfNull(ClangHighlightController c) {
if (c == null) {
return new NullClangHighlightController();
}
return c;
}
// Note: Most of the methods in this class were extracted from the ClangLayoutController class
// and the DecompilerPanel class.
protected Color defaultNonFunctionBackgroundColor = new Color(220, 220, 220);
protected Color defaultHighlightColor = new Color(255, 255, 0, 128); // Default color for highlighting tokens
protected Color defaultSpecialColor = new Color(255, 100, 0, 128); // Default color for specially highlighted tokens
protected Color defaultParenColor = new Color(255, 255, 0, 128); // Default color for highlighting parentheses
protected Color defaultHighlightColor = DEFAULT_HIGHLIGHT_COLOR;
protected Color defaultParenColor = DEFAULT_HIGHLIGHT_COLOR;
protected HashSet<ClangToken> highlightTokenSet = new HashSet<>();
private TokenHighlights primaryHighlightTokens = new TokenHighlights();
private TokenHighlights secondaryHighlightTokens = new TokenHighlights();
private TokenHighlightColors secondaryHighlightColors = new TokenHighlightColors();
protected ArrayList<ClangHighlightListener> highlightListenerList = new ArrayList<>();
public ClangHighlightController() {
}
private List<ClangHighlightListener> listeners = new ArrayList<>();
public abstract void fieldLocationChanged(FieldLocation location, Field field,
EventTrigger trigger);
void loadOptions(DecompileOptions options) {
Color currentVariableHighlightColor = options.getCurrentVariableHighlightColor();
if (currentVariableHighlightColor != null) {
setDefaultHighlightColor(currentVariableHighlightColor);
}
}
public void setDefaultHighlightColor(Color highlightColor) {
defaultHighlightColor = highlightColor;
notifyListeners();
}
public void setDefaultSpecialColor(Color specialColor) {
defaultSpecialColor = specialColor;
notifyListeners();
}
public void setDefaultParenColor(Color parenColor) {
defaultParenColor = parenColor;
notifyListeners();
}
public Color getDefaultHighlightColor() {
return defaultHighlightColor;
}
public Color getDefaultSpecialColor() {
return defaultSpecialColor;
}
public Color getDefaultParenColor() {
return defaultParenColor;
void setHighlightColor(Color c) {
defaultHighlightColor = c;
}
public String getHighlightedText() {
@ -91,144 +83,287 @@ public abstract class ClangHighlightController {
return null;
}
public TokenHighlightColors getSecondaryHighlightColors() {
return secondaryHighlightColors;
}
public TokenHighlights getPrimaryHighlightedTokens() {
return primaryHighlightTokens;
}
public TokenHighlights getSecondaryHighlightedTokens() {
return secondaryHighlightTokens;
}
/**
* Return the current highlighted token (if exists and unique)
* @return token or null
*/
public ClangToken getHighlightedToken() {
if (highlightTokenSet.size() == 1) {
ClangToken[] tokenArray =
highlightTokenSet.toArray(new ClangToken[highlightTokenSet.size()]);
return tokenArray[0];
private ClangToken getHighlightedToken() {
if (primaryHighlightTokens.size() == 1) {
HighlightToken hlToken = CollectionUtils.any(primaryHighlightTokens);
return hlToken.getToken();
}
return null;
}
public void addVarnodesToHighlight(ClangNode parentNode, Set<Varnode> varnodes,
Color highlightColor, Varnode specificvn, PcodeOp specificop, Color specialColor) {
int nchild = parentNode.numChildren();
for (int i = 0; i < nchild; ++i) {
private void gatherAllTokens(ClangNode parentNode, Set<ClangToken> results) {
int n = parentNode.numChildren();
for (int i = 0; i < n; i++) {
ClangNode node = parentNode.Child(i);
if (node.numChildren() > 0) {
addVarnodesToHighlight(node, varnodes, highlightColor, specificvn, specificop,
specialColor);
gatherAllTokens(node, results);
}
else if (node instanceof ClangToken) {
ClangToken tok = (ClangToken) node;
Varnode vn = DecompilerUtils.getVarnodeRef(tok);
if (varnodes.contains(vn)) {
addHighlight(tok, highlightColor);
}
if (vn == specificvn) { // Look for specific varnode to label with specialColor
if ((specificop != null) && (tok.getPcodeOp() == specificop)) {
addHighlight(tok, specialColor);
}
}
results.add((ClangToken) node);
}
}
}
public void clearPrimaryHighlights() {
doClearHighlights(primaryHighlightTokens);
notifyListeners();
}
public void clearAllHighlights() {
doClearHighlights(primaryHighlightTokens);
doClearHighlights(secondaryHighlightTokens);
notifyListeners();
}
private void doClearHighlights(TokenHighlights tokenHighlights) {
Iterator<HighlightToken> it = tokenHighlights.iterator();
while (it.hasNext()) {
HighlightToken highlight = it.next();
it.remove();
ClangToken token = highlight.getToken();
token.setMatchingToken(false);
updateHighlightColor(token);
}
tokenHighlights.clear();
}
public void togglePrimaryHighlights(Color hlColor, Supplier<List<ClangToken>> tokens) {
boolean isAllHighlighted = true;
for (ClangToken otherToken : tokens.get()) {
if (!hasPrimaryHighlight(otherToken)) {
isAllHighlighted = false;
break;
}
}
// this is a bit odd, but whenever we change the primary highlights, we always reset any
// previous primary highlight (see javadoc header)
clearPrimaryHighlights();
if (isAllHighlighted) {
// nothing to do; we toggled from 'all on' to 'all off'
return;
}
addPrimaryHighlights(tokens, hlColor);
}
public boolean hasPrimaryHighlight(ClangToken token) {
return primaryHighlightTokens.contains(token);
}
public boolean hasSecondaryHighlight(ClangToken token) {
return secondaryHighlightTokens.contains(token);
}
public Set<HighlightToken> getSecondaryHighlightsByFunction(
ghidra.program.model.listing.Function f) {
Set<HighlightToken> highlights = secondaryHighlightTokens.getHighlightsByFunction(f);
return highlights;
}
public void removeSecondaryHighlights(ghidra.program.model.listing.Function f) {
Set<HighlightToken> oldHighlights = secondaryHighlightTokens.removeHighlightsByFunction(f);
for (HighlightToken hl : oldHighlights) {
ClangToken token = hl.getToken();
updateHighlightColor(token);
}
notifyListeners();
}
public void addPcodeOpsToHighlight(ClangNode parentNode, Set<PcodeOp> ops,
Color highlightColor) {
int nchild = parentNode.numChildren();
for (int i = 0; i < nchild; ++i) {
ClangNode node = parentNode.Child(i);
if (node.numChildren() > 0) {
addPcodeOpsToHighlight(node, ops, highlightColor);
}
else if (node instanceof ClangToken) {
ClangToken tok = (ClangToken) node;
PcodeOp op = tok.getPcodeOp();
if (ops.contains(op)) {
addHighlight(tok, highlightColor);
}
}
public void removeSecondaryHighlights(ClangToken token) {
secondaryHighlightTokens.remove(token);
}
public void removeSecondaryHighlights(Supplier<? extends Collection<ClangToken>> tokens) {
for (ClangToken clangToken : tokens.get()) {
secondaryHighlightTokens.remove(clangToken);
updateHighlightColor(clangToken);
}
notifyListeners();
}
public void addTokensToHighlights(List<ClangToken> tokenList, Color highlightColor) {
for (ClangToken clangToken : tokenList) {
doAddHighlight(clangToken, highlightColor);
public void addSecondaryHighlights(String tokenText,
Supplier<? extends Collection<ClangToken>> tokens) {
Color highlightColor = secondaryHighlightColors.getColor(tokenText);
addSecondaryHighlights(tokens, highlightColor);
}
public void addSecondaryHighlights(Supplier<? extends Collection<ClangToken>> tokens,
Color hlColor) {
Function<ClangToken, Color> colorProvider = token -> hlColor;
addTokensToHighlights(tokens.get(), colorProvider, secondaryHighlightTokens);
}
@Deprecated // for use by subclasses
protected void addHighlight(ClangToken tok, Color hlColor) {
addPrimaryHighlight(tok, hlColor);
}
public void addPrimaryHighlights(Supplier<? extends Collection<ClangToken>> tokens,
Color hlColor) {
Function<ClangToken, Color> colorProvider = token -> hlColor;
addTokensToHighlights(tokens.get(), colorProvider, primaryHighlightTokens);
}
public void addPrimaryHighlights(ClangNode parentNode,
TokenHighlightColorProvider colorProvider) {
Set<ClangToken> tokens = new HashSet<>();
gatherAllTokens(parentNode, tokens);
addTokensToHighlights(tokens, colorProvider::getColor, primaryHighlightTokens);
}
public void addPrimaryHighlights(ClangNode parentNode, Set<PcodeOp> ops, Color hlColor) {
addPrimaryHighlights(parentNode, token -> {
PcodeOp op = token.getPcodeOp();
return ops.contains(op) ? hlColor : null;
});
}
private void addPrimaryHighlights(Collection<ClangToken> tokens, Color hlColor) {
Function<ClangToken, Color> colorProvider = token -> hlColor;
addTokensToHighlights(tokens, colorProvider, primaryHighlightTokens);
}
private void addTokensToHighlights(Collection<ClangToken> tokens,
Function<ClangToken, Color> colorProvider, TokenHighlights currentHighlights) {
for (ClangToken clangToken : tokens) {
Color color = colorProvider.apply(clangToken);
doAddHighlight(clangToken, color, currentHighlights);
}
notifyListeners();
}
public void clearHighlights() {
for (ClangToken clangToken : highlightTokenSet) {
clangToken.setHighlight(null);
if (clangToken.isMatchingToken()) {
clangToken.setMatchingToken(false);
protected void addPrimaryHighlight(ClangToken token, Color highlightColor) {
addPrimaryHighlights(Set.of(token), highlightColor);
}
private void doAddHighlight(ClangToken clangToken, Color highlightColor,
TokenHighlights currentHighlights) {
if (highlightColor == null) {
return;
}
// store the actual requested color
currentHighlights.add(new HighlightToken(clangToken, highlightColor));
updateHighlightColor(clangToken);
}
private void updateHighlightColor(ClangToken t) {
// set the color to the current combined value of both highlight types
Color combinedColor = getCombinedColor(t);
t.setHighlight(combinedColor);
}
public Color getCombinedColor(ClangToken t) {
HighlightToken primaryHl = primaryHighlightTokens.get(t);
HighlightToken secondaryHl = secondaryHighlightTokens.get(t);
Color primary = primaryHl == null ? null : primaryHl.getColor();
Color secondary = secondaryHl == null ? null : secondaryHl.getColor();
if (primary == null) {
if (secondary == null) {
return null;
}
return secondary;
}
highlightTokenSet.clear();
notifyListeners();
}
public void addHighlight(ClangToken clangToken, Color highlightColor) {
doAddHighlight(clangToken, highlightColor);
notifyListeners();
}
if (secondary == null) {
return primary;
}
public void doAddHighlight(ClangToken clangToken, Color highlightColor) {
clangToken.setHighlight(highlightColor);
highlightTokenSet.add(clangToken);
}
public void clearHighlight(ClangToken clangToken) {
clangToken.setHighlight(null);
highlightTokenSet.remove(clangToken);
notifyListeners();
}
public boolean isHighlighted(ClangToken clangToken) {
return highlightTokenSet.contains(clangToken);
return ColorUtils.blend(primary, secondary, .8f);
}
/**
* If input token is a parenthesis, highlight all
* tokens between it and its match
* @param tok = potential parenthesis token
* @param tok potential parenthesis token
* @param highlightColor the highlight color
* @return a list of all tokens that were highlighted.
*/
public List<ClangToken> addHighlightParen(ClangSyntaxToken tok, Color highlightColor) {
ArrayList<ClangToken> tokenList = new ArrayList<>();
protected List<ClangToken> addPrimaryHighlightToTokensForParenthesis(ClangSyntaxToken tok,
Color highlightColor) {
int paren = tok.getOpen();
if (paren == -1) {
paren = tok.getClose();
}
if (paren == -1) {
return tokenList; // Not a parenthesis
return new ArrayList<>(); // Not a parenthesis
}
List<ClangToken> results = gatherContentsOfParenthesis(tok, paren);
addPrimaryHighlights(results, highlightColor);
return results;
}
private List<ClangToken> gatherContentsOfParenthesis(ClangSyntaxToken tok, int parenId) {
List<ClangToken> results = new ArrayList<>();
int parenCount = 0;
ClangNode par = tok.Parent();
while (par != null) {
boolean outside = true;
if (par instanceof ClangTokenGroup) {
ArrayList<ClangNode> list = new ArrayList<>();
((ClangTokenGroup) par).flatten(list);
for (int i = 0; i < list.size(); ++i) {
ClangToken tk = (ClangToken) list.get(i);
if (tk instanceof ClangSyntaxToken) {
ClangSyntaxToken syn = (ClangSyntaxToken) tk;
if (syn.getOpen() == paren) {
outside = false;
}
else if (syn.getClose() == paren) {
outside = true;
addHighlight(syn, highlightColor);
tokenList.add(syn);
}
if (!(par instanceof ClangTokenGroup)) {
par = par.Parent();
continue;
}
List<ClangNode> list = new ArrayList<>();
((ClangTokenGroup) par).flatten(list);
for (ClangNode node : list) {
ClangToken tk = (ClangToken) node;
if (tk instanceof ClangSyntaxToken) {
ClangSyntaxToken syn = (ClangSyntaxToken) tk;
if (syn.getOpen() == parenId) {
parenCount++;
outside = false;
}
if (!outside) {
addHighlight(tk, highlightColor);
tokenList.add(tk);
else if (syn.getClose() == parenId) {
parenCount++;
outside = true;
results.add(syn);
}
}
if (!outside) {
results.add(tk);
}
if (parenCount == 2) {
return results; // found both parens; break out early
}
}
par = par.Parent();
}
return tokenList;
return results;
}
public void addHighlightBrace(ClangSyntaxToken token, Color highlightColor) {
@ -244,76 +379,20 @@ public abstract class ClangHighlightController {
ClangSyntaxToken matchingBrace = DecompilerUtils.getMatchingBrace(startToken);
if (matchingBrace != null) {
matchingBrace.setMatchingToken(true); // this is a signal to the painter
addHighlight(matchingBrace, highlightColor);
addPrimaryHighlights(Set.of(matchingBrace), highlightColor);
}
}
/**
* Add highlighting to tokens that are surrounded by
* highlighted tokens, but which have no address
*/
public void addHighlightFill() {
ClangTokenGroup lastgroup = null;
ArrayList<ClangNode> newhi = new ArrayList<>();
ArrayList<Color> newcolor = new ArrayList<>();
for (ClangToken tok : highlightTokenSet) {
if (tok.Parent() instanceof ClangTokenGroup) {
ClangTokenGroup par = (ClangTokenGroup) tok.Parent();
if (par == lastgroup) {
continue;
}
lastgroup = par;
int beg = -1;
int end = par.numChildren();
for (int j = 0; j < par.numChildren(); ++j) {
if (par.Child(j) instanceof ClangToken) {
ClangToken token = (ClangToken) par.Child(j);
Color curcolor = token.getHighlight();
if (curcolor != null) {
if (beg == -1) {
beg = j;
}
else {
end = j;
for (int k = beg + 1; k < end; ++k) {
if (par.Child(k) instanceof ClangToken) {
newhi.add(par.Child(k));
newcolor.add(curcolor);
}
}
beg = j;
}
}
else if (token.getMinAddress() != null) {
beg = -1;
}
}
else {
beg = -1;
}
}
}
}
for (int i = 0; i < newhi.size(); ++i) {
ClangToken tok = (ClangToken) newhi.get(i);
if (tok.getHighlight() != null) {
continue;
}
addHighlight(tok, newcolor.get(i));
}
notifyListeners();
public void addListener(ClangHighlightListener listener) {
listeners.add(listener);
}
public boolean addListener(ClangHighlightListener listener) {
return highlightListenerList.add(listener);
}
public boolean removeListener(ClangHighlightListener listener) {
return highlightListenerList.remove(listener);
public void removeListener(ClangHighlightListener listener) {
listeners.remove(listener);
}
private void notifyListeners() {
for (ClangHighlightListener listener : highlightListenerList) {
for (ClangHighlightListener listener : listeners) {
listener.tokenHighlightsChanged();
}
}

View file

@ -20,6 +20,7 @@ import ghidra.program.model.listing.Function;
import ghidra.program.util.ProgramLocation;
import ghidra.program.util.ProgramSelection;
import ghidra.util.bean.field.AnnotatedTextFieldElement;
import utility.function.Callback;
public interface DecompilerCallbackHandler {
@ -44,4 +45,6 @@ public interface DecompilerCallbackHandler {
void exportLocation();
void goToFunction(Function function, boolean newWindow);
void doWheNotBusy(Callback c);
}

View file

@ -30,6 +30,7 @@ import ghidra.program.model.pcode.HighFunction;
import ghidra.program.util.ProgramLocation;
import ghidra.program.util.ProgramSelection;
import ghidra.util.bean.field.AnnotatedTextFieldElement;
import utility.function.Callback;
/**
* Coordinates the interactions between the DecompilerProvider, DecompilerPanel, and the DecompilerManager
@ -93,6 +94,7 @@ public class DecompilerController {
* @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 viewerPosition the viewer position
*/
public void display(Program program, ProgramLocation location, ViewerPosition viewerPosition) {
if (!decompilerMgr.isBusy() && decompilerPanel.containsLocation(location)) {
@ -121,7 +123,7 @@ public class DecompilerController {
return false;
}
// cancel any pending decompile tasks, so that previous requests don't overwrite the latest request
// cancel pending decompile tasks; previous requests shouldn't overwrite the latest request
decompilerMgr.cancelAll();
setDecompileData(
new DecompileData(program, function, location, results, null, null, viewerPosition));
@ -135,7 +137,7 @@ public class DecompilerController {
/**
* Sets new decompiler options and triggers a new decompile.
* @param decompilerOptions
* @param decompilerOptions the options
*/
public void setOptions(DecompileOptions decompilerOptions) {
clearCache();
@ -168,7 +170,8 @@ public class DecompilerController {
//==================================================================================================
/**
* Called by the DecompilerManager to update the currently displayed DecompileData.
* Called by the DecompilerManager to update the currently displayed DecompileData
* @param decompileData the new data
*/
public void setDecompileData(DecompileData decompileData) {
updateCache(decompileData);
@ -193,12 +196,18 @@ public class DecompilerController {
//==================================================================================================
// Methods called by actions and other miscellaneous classes
//==================================================================================================
public void doWhenNotBusy(Callback c) {
callbackHandler.doWheNotBusy(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 debugFile the debug file
*/
public void refreshDisplay(Program program, ProgramLocation location, File debugFile) {
clearCache();
@ -304,5 +313,4 @@ public class DecompilerController {
}
}
}
}

View file

@ -20,6 +20,9 @@ import java.awt.event.MouseEvent;
import java.math.BigInteger;
import java.util.*;
import java.util.List;
import java.util.Map.Entry;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import javax.swing.JComponent;
import javax.swing.JPanel;
@ -40,6 +43,7 @@ import ghidra.app.decompiler.*;
import ghidra.app.decompiler.component.hover.DecompilerHoverService;
import ghidra.app.plugin.core.decompile.DecompilerClipboardProvider;
import ghidra.app.plugin.core.decompile.actions.FieldBasedSearchLocation;
import ghidra.app.plugin.core.decompile.actions.TokenHighlightColorProvider;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Program;
@ -48,6 +52,8 @@ import ghidra.program.util.ProgramLocation;
import ghidra.program.util.ProgramSelection;
import ghidra.util.*;
import ghidra.util.bean.field.AnnotatedTextFieldElement;
import ghidra.util.task.SwingUpdateManager;
import util.CollectionUtils;
/**
* Class to handle the display of a decompiled function
@ -65,12 +71,21 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
private DecompilerFieldPanel fieldPanel;
private ClangLayoutController layoutMgr;
private HighlightFactory hlFactory;
private ClangHighlightController highlightController;
private PendingHighlightUpdate pendingHighlightUpdate;
private SwingUpdateManager highlighCursorUpdater = new SwingUpdateManager(() -> {
if (pendingHighlightUpdate != null) {
pendingHighlightUpdate.doUpdate();
pendingHighlightUpdate = null;
}
});
private int currentMiddleMouseHighlightButton;
private Color currentSearchHighlightColor;
private Color currentHighlightColor;
private int middleMouseHighlightButton;
private Color middleMouseHighlightColor;
private Color currentVariableHighlightColor;
private Color searchHighlightColor;
private SearchLocation currentSearchLocation;
private DecompileData decompileData = new EmptyDecompileData("No Function");
@ -104,9 +119,10 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
decompilerHoverProvider = new DecompilerHoverProvider();
currentSearchHighlightColor = options.getSearchHighlightColor();
currentHighlightColor = options.getMiddleMouseHighlightColor();
currentMiddleMouseHighlightButton = options.getMiddleMouseHighlightButton();
searchHighlightColor = options.getSearchHighlightColor();
currentVariableHighlightColor = options.getCurrentVariableHighlightColor();
middleMouseHighlightColor = options.getMiddleMouseHighlightColor();
middleMouseHighlightButton = options.getMiddleMouseHighlightButton();
setLayout(new BorderLayout());
add(scroller);
@ -128,6 +144,67 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
return fieldPanel;
}
public void applySecondaryHighlights(Map<String, Color> highlightsByName) {
Set<Entry<String, Color>> entries = highlightsByName.entrySet();
for (Entry<String, Color> entry : entries) {
String tokenName = entry.getKey();
Color color = entry.getValue();
Supplier<List<ClangToken>> lazyTokens = () -> findTokensByName(tokenName);
highlightController.addSecondaryHighlights(lazyTokens, color);
}
}
public TokenHighlightColors getSecondaryHighlightColors() {
return highlightController.getSecondaryHighlightColors();
}
public TokenHighlights getSecondaryHighlightedTokens() {
return highlightController.getSecondaryHighlightedTokens();
}
public void removeSecondaryHighlights() {
Function function = controller.getFunction();
highlightController.removeSecondaryHighlights(function);
}
public void removeSecondaryHighlight(ClangToken token) {
removeSecondaryHighlight(token.getText());
}
private void removeSecondaryHighlight(String tokenText) {
Supplier<List<ClangToken>> lazyTokens = () -> findTokensByName(tokenText);
highlightController.removeSecondaryHighlights(lazyTokens);
}
public void addSecondaryHighlight(ClangToken token) {
String tokenText = token.getText();
addSecondaryHighlight(tokenText);
}
private void addSecondaryHighlight(String tokenText) {
Supplier<List<ClangToken>> lazyTokens = () -> {
return findTokensByName(tokenText);
};
highlightController.addSecondaryHighlights(tokenText, lazyTokens);
}
public void addSecondaryHighlight(ClangToken token, Color color) {
addSecondaryHighlight(token.getText(), color);
}
private void addSecondaryHighlight(String tokenText, Color color) {
Supplier<List<ClangToken>> lazyTokens = () -> findTokensByName(tokenText);
highlightController.addSecondaryHighlights(lazyTokens, color);
}
private void togglePrimaryHighlight(FieldLocation location, Field field, Color highlightColor) {
ClangToken token = ((ClangTextField) field).getToken(location);
Supplier<List<ClangToken>> lazyTokens = () -> findTokensByName(token.getText());
highlightController.togglePrimaryHighlights(middleMouseHighlightColor, lazyTokens);
}
@Override
public void setBackground(Color bg) {
originalBackgroundColor = bg;
@ -168,17 +245,48 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
decompilerHoverProvider.setProgram(decompileData.getProgram());
/*
* Give user notice when seeing the decompile of a non-function.
*/
// give user notice when seeing the decompile of a non-function
useNonFunctionColor = function instanceof UndefinedFunction;
setBackground(originalBackgroundColor);
if (clipboard != null) {
clipboard.selectionChanged(null);
}
// don't highlight search results across functions
currentSearchLocation = null;
reapplySecondaryHighlights();
}
private void reapplySecondaryHighlights() {
Function function = decompileData.getFunction();
if (function == null) {
return;
}
// The existing highlights are based on the previously generated tokens, which no longer
// exist. Use those tokens to highlight the current tokens, which are conceptually the
// same tokens.
Set<HighlightToken> oldHighlights =
highlightController.getSecondaryHighlightsByFunction(function);
//@formatter:off
Map<String, List<ClangToken>> tokensByName =
CollectionUtils.asStream(oldHighlights)
.map(ht -> ht.getToken())
.collect(Collectors.groupingBy(t -> t.getText()))
;
//@formatter:on
Set<Entry<String, List<ClangToken>>> entries = tokensByName.entrySet();
for (Entry<String, List<ClangToken>> entry : entries) {
String name = entry.getKey();
List<ClangToken> oldTokens = entry.getValue();
highlightController.removeSecondaryHighlights(() -> oldTokens);
addSecondaryHighlight(name);
}
}
private void setLocation(DecompileData oldData, DecompileData newData) {
@ -424,9 +532,9 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
public void dispose() {
setDecompileData(new EmptyDecompileData("Disposed"));
layoutMgr = null;
decompilerHoverProvider.dispose();
highlighCursorUpdater.dispose();
highlightController.clearAllHighlights();
}
private FontMetrics getFontMetrics(DecompileOptions decompileOptions) {
@ -464,8 +572,8 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
}
}
if (buttonState == currentMiddleMouseHighlightButton && clickCount == 1) {
highlightVariable(location, field, currentHighlightColor);
if (buttonState == middleMouseHighlightButton && clickCount == 1) {
togglePrimaryHighlight(location, field, middleMouseHighlightColor);
}
}
@ -624,17 +732,6 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
}
}
private void highlightVariable(FieldLocation location, Field field, Color highlightColor) {
if (highlightController != null) {
ClangToken token = ((ClangTextField) field).getToken(location);
List<ClangToken> tokenList = new ArrayList<>();
findTokensByName(tokenList, layoutMgr.getRoot(), token.getText());
highlightController.clearHighlights();
highlightController.addTokensToHighlights(tokenList, highlightColor);
repaint();
}
}
Program getProgram() {
return decompileData.getProgram();
}
@ -654,9 +751,8 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
return;
}
if (highlightController != null) {
highlightController.fieldLocationChanged(location, field, trigger);
}
pendingHighlightUpdate = new PendingHighlightUpdate(location, field, trigger);
highlighCursorUpdater.update();
if (!(field instanceof ClangTextField)) {
return;
@ -743,19 +839,25 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
// End Search Methods
//==================================================================================================
public Color getDefaultHighlightColor() {
return currentHighlightColor;
public Color getCurrentVariableHighlightColor() {
return currentVariableHighlightColor;
}
public Color getDefaultSpecialColor() {
public Color getMiddleMouseHighlightColor() {
return middleMouseHighlightColor;
}
/**
* The color used in a primary highlight to mark the token that was clicked. This is used
* in 'slice' actions to mark the source of the slice.
* @return the color
*/
public Color getSpecialHighlightColor() {
return SPECIAL_COLOR_DEF;
}
public String getHighlightedText() {
if (highlightController != null) {
return highlightController.getHighlightedText();
}
return null;
return highlightController.getHighlightedText();
}
public FieldLocation getCursorPosition() {
@ -797,6 +899,10 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
return ((ClangTextField) field).getToken(cursorPosition);
}
public DecompileOptions getOptions() {
return options;
}
public void addHoverService(DecompilerHoverService hoverService) {
decompilerHoverProvider.addHoverService(hoverService);
}
@ -819,38 +925,40 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
return decompilerHoverProvider.isShowing();
}
public void clearHighlights() {
if (highlightController != null) {
highlightController.clearHighlights();
}
public void clearPrimaryHighlights() {
highlightController.clearPrimaryHighlights();
}
public void addVarnodeHighlights(Set<Varnode> varnodes, Color highlightColor,
Varnode specificvn, PcodeOp specificop, Color specialColor) {
if (highlightController != null) {
ClangTokenGroup root = layoutMgr.getRoot();
highlightController.addVarnodesToHighlight(root, varnodes, highlightColor, specificvn,
specificop, specialColor);
}
public void addVarnodeHighlights(Set<Varnode> varnodes,
TokenHighlightColorProvider colorProvider) {
ClangTokenGroup root = layoutMgr.getRoot();
highlightController.addPrimaryHighlights(root, colorProvider);
}
public void addPcodeOpHighlights(Set<PcodeOp> ops, Color highlightColor) {
if (highlightController != null) {
ClangTokenGroup root = layoutMgr.getRoot();
highlightController.addPcodeOpsToHighlight(root, ops, highlightColor);
}
public void addPcodeOpHighlights(Set<PcodeOp> ops, Color hlColor) {
ClangTokenGroup root = layoutMgr.getRoot();
highlightController.addPrimaryHighlights(root, ops, hlColor);
}
private void findTokensByName(List<ClangToken> tokenList, ClangTokenGroup group, String name) {
public List<ClangToken> findTokensByName(String name) {
List<ClangToken> tokens = new ArrayList<>();
doFindTokensByName(tokens, layoutMgr.getRoot(), name);
return tokens;
}
private void doFindTokensByName(List<ClangToken> tokens, ClangTokenGroup group, String name) {
// TODO is it possible that two or more different variable tokens share the same name?
for (int i = 0; i < group.numChildren(); ++i) {
ClangNode child = group.Child(i);
if (child instanceof ClangTokenGroup) {
findTokensByName(tokenList, (ClangTokenGroup) child, name);
doFindTokensByName(tokens, (ClangTokenGroup) child, name);
}
else if (child instanceof ClangToken) {
ClangToken token = (ClangToken) child;
if (name.equals(token.getText())) {
tokenList.add(token);
tokens.add(token);
}
}
}
@ -875,23 +983,28 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
FieldSelection selection = new FieldSelection();
selection.addRange(BigInteger.ZERO, numIndexes);
fieldPanel.setSelection(selection);
// fake it out that the selection was caused by the field panel GUI.
selectionChanged(selection, EventTrigger.GUI_ACTION);
}
public void optionsChanged(DecompileOptions decompilerOptions) {
setBackground(decompilerOptions.getCodeViewerBackgroundColor());
currentHighlightColor = decompilerOptions.getMiddleMouseHighlightColor();
currentMiddleMouseHighlightButton = decompilerOptions.getMiddleMouseHighlightButton();
currentSearchHighlightColor = decompilerOptions.getSearchHighlightColor();
if (highlightController != null) {
highlightController.loadOptions(decompilerOptions);
}
currentVariableHighlightColor = options.getCurrentVariableHighlightColor();
middleMouseHighlightColor = decompilerOptions.getMiddleMouseHighlightColor();
middleMouseHighlightButton = decompilerOptions.getMiddleMouseHighlightButton();
searchHighlightColor = decompilerOptions.getSearchHighlightColor();
highlightController.setHighlightColor(currentVariableHighlightColor);
}
public void setHighlightController(ClangHighlightController highlightController) {
this.highlightController = highlightController;
highlightController.loadOptions(options);
if (this.highlightController != null) {
this.highlightController.removeListener(this);
}
this.highlightController = ClangHighlightController.dummyIfNull(highlightController);
highlightController.setHighlightColor(currentVariableHighlightColor);
highlightController.addListener(this);
}
@ -900,6 +1013,36 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
repaint();
}
/**
* This is function is used to alert the panel that a token was renamed.
* If the token that is being renamed had a secondary highlight, we must re-apply the highlight
* to the new token.
*
* @param token the token being renamed
* @param newName the new name of the token
*/
public void tokenRenamed(ClangToken token, String newName) {
if (!highlightController.hasSecondaryHighlight(token)) {
return;
}
TokenHighlightColors colors = highlightController.getSecondaryHighlightColors();
String oldName = token.getText();
Color hlColor = colors.getColor(oldName);
highlightController.removeSecondaryHighlights(token);
controller.doWhenNotBusy(() -> {
Supplier<List<ClangToken>> lazyTokens = () -> findTokensByName(newName);
highlightController.addSecondaryHighlights(lazyTokens, hlColor);
});
}
public ClangHighlightController getHighlightController() {
return highlightController;
}
//==================================================================================================
// Inner Classes
//==================================================================================================
@ -924,7 +1067,7 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
}
return new Highlight[] { new Highlight(currentSearchLocation.getStartIndexInclusive(),
currentSearchLocation.getEndIndexInclusive(), currentSearchHighlightColor) };
currentSearchLocation.getEndIndexInclusive(), searchHighlightColor) };
}
}
@ -998,4 +1141,25 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
EventTrigger.GUI_ACTION);
}
}
/**
* A class to track pending location updates. This allows us to buffer updates, only sending
* the last one received.
*/
private class PendingHighlightUpdate {
private FieldLocation location;
private Field field;
private EventTrigger trigger;
PendingHighlightUpdate(FieldLocation location, Field field, EventTrigger trigger) {
this.location = location;
this.field = field;
this.trigger = trigger;
}
void doUpdate() {
highlightController.fieldLocationChanged(location, field, trigger);
}
}
}

View file

@ -0,0 +1,47 @@
/* ###
* 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;
import java.awt.Color;
import ghidra.app.decompiler.ClangToken;
/**
* A class to used to track a {@link Decompiler} token along with its highlight color
*/
public class HighlightToken {
private ClangToken token;
private Color color;
public HighlightToken(ClangToken token, Color color) {
this.token = token;
this.color = color;
}
public ClangToken getToken() {
return token;
}
public Color getColor() {
return color;
}
@Override
public String toString() {
return token.toString() + "; color=" + color;
}
}

View file

@ -15,25 +15,21 @@
*/
package ghidra.app.decompiler.component;
import ghidra.app.decompiler.ClangSyntaxToken;
import ghidra.app.decompiler.ClangToken;
import docking.widgets.EventTrigger;
import docking.widgets.fieldpanel.field.Field;
import docking.widgets.fieldpanel.support.FieldLocation;
import ghidra.app.decompiler.ClangSyntaxToken;
import ghidra.app.decompiler.ClangToken;
/**
* Class to handle location based highlights for a decompiled function.
*/
public class LocationClangHighlightController extends ClangHighlightController {
public LocationClangHighlightController() {
}
@Override
public void fieldLocationChanged(FieldLocation location, Field field, EventTrigger trigger) {
clearHighlights();
clearPrimaryHighlights();
if (!(field instanceof ClangTextField)) {
return;
@ -44,12 +40,9 @@ public class LocationClangHighlightController extends ClangHighlightController {
return;
}
// // clear any highlighted searchResults
// decompilerPanel.setSearchResults(null);
addHighlight(tok, defaultHighlightColor);
addPrimaryHighlight(tok, defaultHighlightColor);
if (tok instanceof ClangSyntaxToken) {
addHighlightParen((ClangSyntaxToken) tok, defaultParenColor);
addPrimaryHighlightToTokensForParenthesis((ClangSyntaxToken) tok, defaultParenColor);
addHighlightBrace((ClangSyntaxToken) tok, defaultParenColor);
}
}

View file

@ -0,0 +1,81 @@
/* ###
* 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;
import java.awt.Color;
import java.util.Collection;
import java.util.Set;
import java.util.function.Supplier;
import docking.widgets.EventTrigger;
import docking.widgets.fieldpanel.field.Field;
import docking.widgets.fieldpanel.support.FieldLocation;
import ghidra.app.decompiler.*;
import ghidra.app.plugin.core.decompile.actions.TokenHighlightColorProvider;
import ghidra.program.model.pcode.PcodeOp;
/**
* A stub implementation of the highlight controller that allows clients to avoid null checks
*/
public class NullClangHighlightController extends ClangHighlightController {
@Override
public void fieldLocationChanged(FieldLocation location, Field field, EventTrigger trigger) {
// stub
}
@Override
public String getHighlightedText() {
return null;
}
@Override
public void addPrimaryHighlights(ClangNode parentNode,
TokenHighlightColorProvider colorProvider) {
// stub
}
@Override
public void addPrimaryHighlights(ClangNode parentNode, Set<PcodeOp> ops, Color highlightColor) {
// stub
}
@Override
public void addPrimaryHighlights(Supplier<? extends Collection<ClangToken>> tokens,
Color highlightColor) {
// stub
}
@Override
public void clearAllHighlights() {
// stub
}
@Override
public void addHighlightBrace(ClangSyntaxToken token, Color highlightColor) {
// stub
}
@Override
public void addListener(ClangHighlightListener listener) {
// stub
}
@Override
public void removeListener(ClangHighlightListener listener) {
// stub
}
}

View file

@ -0,0 +1,50 @@
/* ###
* 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;
import java.awt.Color;
import java.util.*;
/**
* A class to create and store colors related to token names
*/
public class TokenHighlightColors {
private int minColorSaturation = 100;
private int defaultColorAlpha = 100;
private Map<String, Color> colorsByName = new HashMap<>();
private List<Color> recentColors = new ArrayList<>();
private Color generateColor() {
return new Color((int) (minColorSaturation + Math.random() * (256 - minColorSaturation)),
(int) (minColorSaturation + Math.random() * (256 - minColorSaturation)),
(int) (minColorSaturation + Math.random() * (256 - minColorSaturation)),
defaultColorAlpha);
}
public Color getColor(String text) {
return colorsByName.computeIfAbsent(text, t -> generateColor());
}
public void setColor(String text, Color color) {
colorsByName.put(text, color);
recentColors.add(color);
}
public List<Color> getRecentColors() {
return recentColors;
}
}

View file

@ -0,0 +1,260 @@
/* ###
* 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;
import java.awt.Color;
import java.util.*;
import java.util.Map.Entry;
import ghidra.app.decompiler.*;
import ghidra.program.model.listing.Function;
import ghidra.program.model.pcode.HighFunction;
/**
* A simple class to manage {@link HighlightToken}s used to create highlights in the Decompiler.
* This class allows clients to access highlights either by a {@link ClangToken} or a
* {@link HighlightToken}.
*/
public class TokenHighlights implements Iterable<HighlightToken> {
private Map<TokenKey, HighlightToken> highlightsByToken = new HashMap<>();
public Map<String, Color> copyHighlightsByName() {
Map<String, Color> results = new HashMap<>();
Collection<HighlightToken> values = highlightsByToken.values();
for (HighlightToken hl : values) {
String name = hl.getToken().getText();
results.put(name, hl.getColor());
}
return results;
}
private TokenKey getKey(HighlightToken ht) {
return new TokenKey(ht);
}
private TokenKey getKey(ClangToken t) {
return new TokenKey(t);
}
private Function getFunction(ClangToken t) {
ClangFunction cFunction = t.getClangFunction();
if (cFunction == null) {
return null;
}
HighFunction highFunction = cFunction.getHighFunction();
if (highFunction == null) {
return null;
}
return highFunction.getFunction();
}
/**
* Returns true if there are not highlights
* @return true if there are not highlights
*/
public boolean isEmpty() {
return size() == 0;
}
/**
* Returns the number of highlights
* @return the number of highlights
*/
public int size() {
return highlightsByToken.size();
}
/**
* Adds the given highlight to this container
* @param t the highlight
*/
public void add(HighlightToken t) {
highlightsByToken.put(getKey(t), t);
}
/**
* Gets the current highlight for the given token
* @param t the token
* @return the highlight
*/
public HighlightToken get(ClangToken t) {
return highlightsByToken.get(getKey(t));
}
/**
* Returns all highlights for the given function
*
* @param f the function
* @return the highlights
*/
public Set<HighlightToken> getHighlightsByFunction(Function f) {
Set<HighlightToken> results = new HashSet<>();
Set<TokenKey> keys = getHighlightKeys(f);
for (TokenKey key : keys) {
HighlightToken hl = highlightsByToken.get(key);
results.add(hl);
}
return results;
}
/**
* Returns true if this class has a highlight for the given token
* @param t the token
* @return true if this class has a highlight for the given token
*/
public boolean contains(ClangToken t) {
return highlightsByToken.containsKey(getKey(t));
}
/**
* Removes all highlights from this container
*/
public void clear() {
highlightsByToken.clear();
}
/**
* Removes the highlight for the given token
* @param t the token
*/
public void remove(ClangToken t) {
highlightsByToken.remove(getKey(t));
}
/**
* Removes all highlights associated with the given function
*
* @param function the function
* @return the removed highlights; empty if no highlights existed
*/
public Set<HighlightToken> removeHighlightsByFunction(Function function) {
Set<HighlightToken> oldHighlights = new HashSet<>();
Set<TokenKey> keys = getHighlightKeys(function);
for (TokenKey key : keys) {
HighlightToken hl = highlightsByToken.remove(key);
oldHighlights.add(hl);
}
return oldHighlights;
}
private Set<TokenKey> getHighlightKeys(Function function) {
Set<TokenKey> results = new HashSet<>();
Set<Entry<TokenKey, HighlightToken>> entries = highlightsByToken.entrySet();
for (Entry<TokenKey, HighlightToken> entry : entries) {
HighlightToken highlight = entry.getValue();
ClangToken token = highlight.getToken();
Function tokenFunction = getFunction(token);
if (function.equals(tokenFunction)) {
results.add(entry.getKey());
}
}
return results;
}
@Override
public Iterator<HighlightToken> iterator() {
return highlightsByToken.values().iterator();
}
@Override
public String toString() {
return highlightsByToken.values().toString();
}
//==================================================================================================
// Inner Classes
//==================================================================================================
// a key that allows us to equate tokens that are not the same instance
private class TokenKey {
private ClangToken token;
TokenKey(ClangToken token) {
this.token = Objects.requireNonNull(token);
}
public TokenKey(HighlightToken t) {
this(t.getToken());
}
@Override
public int hashCode() {
String text = token.getText();
return text == null ? 0 : text.hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (getClass() != obj.getClass()) {
return false;
}
ClangToken otherToken = ((TokenKey) obj).token;
if (token.getClass() != otherToken.getClass()) {
return false;
}
if (!Objects.equals(token.getText(), otherToken.getText())) {
return false;
}
ClangLine lineParent = token.getLineParent();
ClangLine otherLineParent = otherToken.getLineParent();
if (!sameLines(lineParent, otherLineParent)) {
return false;
}
if (lineParent == null) {
return false;
}
int positionInLine = lineParent.indexOfToken(token);
int otherPositionInLine = otherLineParent.indexOfToken(otherToken);
return positionInLine == otherPositionInLine;
}
private boolean sameLines(ClangLine l1, ClangLine l2) {
if (l1 == null) {
if (l2 != null) {
return false;
}
return true;
}
else if (l2 == null) {
return false;
}
return l1.getLineNumber() == l2.getLineNumber();
}
@Override
public String toString() {
return token.toString();
}
}
}

View file

@ -20,7 +20,6 @@ import java.util.*;
import org.jdom.Element;
import ghidra.app.CorePluginPackage;
import ghidra.app.decompiler.component.DecompilerHighlightService;
import ghidra.app.decompiler.component.hover.DecompilerHoverService;
import ghidra.app.events.*;
import ghidra.app.plugin.PluginCategoryNames;
@ -49,7 +48,6 @@ import ghidra.util.task.SwingUpdateManager;
GoToService.class, NavigationHistoryService.class, ClipboardService.class,
DataTypeManagerService.class /*, ProgramManager.class */
},
servicesProvided = { DecompilerHighlightService.class },
eventsConsumed = {
ProgramActivatedPluginEvent.class, ProgramOpenedPluginEvent.class,
ProgramLocationPluginEvent.class, ProgramSelectionPluginEvent.class,
@ -83,12 +81,6 @@ public class DecompilePlugin extends Plugin {
disconnectedProviders = new ArrayList<>();
connectedProvider = new PrimaryDecompilerProvider(this);
registerServices();
}
private void registerServices() {
registerServiceProvided(DecompilerHighlightService.class, connectedProvider);
}
@Override
@ -102,10 +94,6 @@ public class DecompilePlugin extends Plugin {
}
}
/**
* Tells the Plugin to write any data-dependent state to the
* output stream.
*/
@Override
public void writeDataState(SaveState saveState) {
if (connectedProvider != null) {
@ -128,11 +116,6 @@ public class DecompilePlugin extends Plugin {
}
}
/**
* Read data state; called after readConfigState(). Events generated
* by plugins we depend on should have been already been thrown by the
* time this method is called.
*/
@Override
public void readDataState(SaveState saveState) {
ProgramManager programManagerService = tool.getService(ProgramManager.class);

View file

@ -23,7 +23,9 @@ import ghidra.app.context.NavigatableActionContext;
import ghidra.app.context.RestrictedAddressSetContext;
import ghidra.app.decompiler.component.DecompilerPanel;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
import ghidra.util.Msg;
import ghidra.util.UndefinedFunction;
import utility.function.Callback;
public class DecompilerActionContext extends NavigatableActionContext
@ -55,6 +57,15 @@ public class DecompilerActionContext extends NavigatableActionContext
return getComponentProvider().getDecompilerPanel();
}
public Function getFunction() {
return getComponentProvider().getController().getFunction();
}
public boolean hasRealFunction() {
Function f = getFunction();
return f != null && !(f instanceof UndefinedFunction);
}
/**
* The companion method of {@link #checkActionEnablement(Supplier)}. Decompiler actions
* must call this method from their {@link DockingActionIf#actionPerformed(ActionContext)}

View file

@ -15,17 +15,17 @@
*/
package ghidra.app.plugin.core.decompile;
import java.awt.Color;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.List;
import java.util.*;
import java.util.concurrent.ConcurrentLinkedQueue;
import javax.swing.*;
import docking.*;
import docking.action.*;
import docking.widgets.fieldpanel.LayoutModel;
import docking.widgets.fieldpanel.support.FieldLocation;
import docking.widgets.fieldpanel.support.ViewerPosition;
import ghidra.GhidraOptions;
@ -51,10 +51,10 @@ import ghidra.util.bean.field.AnnotatedTextFieldElement;
import ghidra.util.task.SwingUpdateManager;
import resources.Icons;
import resources.ResourceManager;
import utility.function.Callback;
public class DecompilerProvider extends NavigatableComponentProviderAdapter
implements DomainObjectListener, OptionsChangeListener, DecompilerCallbackHandler,
DecompilerHighlightService {
implements DomainObjectListener, OptionsChangeListener, DecompilerCallbackHandler {
final static String OPTIONS_TITLE = "Decompiler";
private static Icon REFRESH_ICON = Icons.REFRESH_ICON;
@ -84,6 +84,15 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
private DockingAction renameFunctionAction;
private DockingAction findReferencesAction;
private CloneDecompilerAction cloneDecompilerAction;
private SelectAllAction selectAllAction;
private SetSecondaryHighlightAction setSecondaryHighlightAction;
private SetSecondaryHighlightColorChooserAction setSecondaryHighlightColorChooserAction;
private RemoveAllSecondaryHighlightsAction removeAllSecondadryHighlightsAction;
private RemoveSecondaryHighlightAction removeSecondaryHighlightAction;
private final DecompilePlugin plugin;
private ClipboardService clipboardService;
private DecompilerClipboardProvider clipboardProvider;
@ -97,13 +106,15 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
private DecoratorPanel decorationPanel;
private ClangHighlightController highlightController;
private CloneDecompilerAction cloneDecompilerAction;
private SelectAllAction selectAllAction;
private ViewerPosition pendingViewerPosition;
private SwingUpdateManager redecompilerUpdater;
private SwingUpdateManager redecompileUpdater;
// Follow-up work can be items that need to happen after a pending decompile is finished, such
// as updating highlights after a variable rename
private SwingUpdateManager followUpWorkUpdater;
private Queue<Callback> followUpWork = new ConcurrentLinkedQueue<>();
private ServiceListener serviceListener = new ServiceListener() {
@Override
@ -131,9 +142,11 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
decompilerOptions = new DecompileOptions();
initializeDecompilerOptions();
highlightController = new LocationClangHighlightController();
controller = new DecompilerController(this, decompilerOptions, clipboardProvider);
DecompilerPanel decompilerPanel = controller.getDecompilerPanel();
// TODO move the hl controller into the panel
highlightController = new LocationClangHighlightController();
decompilerPanel.setHighlightController(highlightController);
decorationPanel = new DecoratorPanel(decompilerPanel, isConnected);
@ -155,7 +168,8 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
setHelpLocation(new HelpLocation(plugin.getName(), "Decompiler"));
addToTool();
redecompilerUpdater = new SwingUpdateManager(500, 5000, () -> doRefresh());
redecompileUpdater = new SwingUpdateManager(500, 5000, () -> doRefresh());
followUpWorkUpdater = new SwingUpdateManager(() -> doFollowUpWork());
plugin.getTool().addServiceListener(serviceListener);
}
@ -287,7 +301,7 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
controller.resetDecompiler();
}
redecompilerUpdater.update();
redecompileUpdater.update();
}
@ -300,6 +314,20 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
}
}
private void doFollowUpWork() {
if (isBusy()) {
// try again later
followUpWorkUpdater.updateLater();
return;
}
Callback work = followUpWork.poll();
while (work != null) {
work.call();
work = followUpWork.poll();
}
}
//==================================================================================================
// OptionsListener methods
//==================================================================================================
@ -332,7 +360,8 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
public void dispose() {
super.dispose();
redecompilerUpdater.dispose();
redecompileUpdater.dispose();
followUpWorkUpdater.dispose();
if (clipboardService != null) {
clipboardService.deRegisterClipboardContentProvider(clipboardProvider);
@ -420,8 +449,8 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
return null;
}
boolean isDecompiling() {
return controller.isDecompiling();
boolean isBusy() {
return redecompileUpdater.isBusy() || controller.isDecompiling();
}
/**
@ -457,6 +486,10 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
decompilerPanel.setCursorPosition(location);
}
DecompilerController getController() {
return controller;
}
//==================================================================================================
// methods called from the controller
//==================================================================================================
@ -466,10 +499,6 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
tool.setStatusInfo(message);
}
/**
* Called from the DecompilerController to indicate that the decompilerData has changed.
* @param decompileData the new DecompilerData
*/
@Override
public void decompileDataChanged(DecompileData decompileData) {
updateTitle();
@ -601,6 +630,12 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
}
}
@Override
public void doWheNotBusy(Callback c) {
followUpWork.offer(c);
followUpWorkUpdater.update();
}
//==================================================================================================
// methods called from other members
//==================================================================================================
@ -610,14 +645,28 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
}
public void cloneWindow() {
final DecompilerProvider newProvider = plugin.createNewDisconnectedProvider();
DecompilerProvider newProvider = plugin.createNewDisconnectedProvider();
// invoke later to give the window manage a chance to create the new window
// (its done in an invoke later)
Swing.runLater(() -> {
ViewerPosition myViewPosition = controller.getDecompilerPanel().getViewerPosition();
newProvider.doSetProgram(program);
newProvider.controller.setDecompileData(controller.getDecompileData());
newProvider.setLocation(currentLocation,
controller.getDecompilerPanel().getViewerPosition());
// Any change in the HighlightTokens should be delivered to the new panel
DecompilerPanel myPanel = getDecompilerPanel();
TokenHighlights myHighlights = myPanel.getSecondaryHighlightedTokens();
newProvider.setLocation(currentLocation, myPanel.getViewerPosition());
// transfer any state after the new decompiler is initialized
DecompilerPanel newPanel = newProvider.getDecompilerPanel();
Map<String, Color> highlightsByName = myHighlights.copyHighlightsByName();
newProvider.doWheNotBusy(() -> {
newPanel.setViewerPosition(myViewPosition);
newPanel.applySecondaryHighlights(highlightsByName);
});
});
}
@ -736,21 +785,6 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
editDataTypeAction = new EditDataTypeAction(tool, controller);
setGroupInfo(editDataTypeAction, variableGroup, subGroupPosition++);
defUseHighlightAction = new HighlightDefinedUseAction(controller);
setGroupInfo(defUseHighlightAction, variableGroup, subGroupPosition++);
forwardSliceAction = new ForwardSliceAction(controller);
setGroupInfo(forwardSliceAction, variableGroup, subGroupPosition++);
backwardSliceAction = new BackwardsSliceAction(controller);
setGroupInfo(backwardSliceAction, variableGroup, subGroupPosition++);
forwardSliceToOpsAction = new ForwardSliceToPCodeOpsAction(controller);
setGroupInfo(forwardSliceToOpsAction, variableGroup, subGroupPosition++);
backwardSliceToOpsAction = new BackwardsSliceToPCodeOpsAction(controller);
setGroupInfo(backwardSliceToOpsAction, variableGroup, subGroupPosition++);
//
// Listing action for Creating Structure on a Variable
//
@ -768,18 +802,51 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
lockLocalAction = new CommitLocalsAction(tool, controller);
setGroupInfo(lockLocalAction, commitGroup, subGroupPosition++);
subGroupPosition = 0; // reset for the next group
//
// Highlight
//
String highlightGroup = "4a - Highlight Group";
tool.setMenuGroup(new String[] { "Highlight" }, highlightGroup);
defUseHighlightAction = new HighlightDefinedUseAction(controller);
setGroupInfo(defUseHighlightAction, highlightGroup, subGroupPosition++);
forwardSliceAction = new ForwardSliceAction(controller);
setGroupInfo(forwardSliceAction, highlightGroup, subGroupPosition++);
backwardSliceAction = new BackwardsSliceAction();
setGroupInfo(backwardSliceAction, highlightGroup, subGroupPosition++);
forwardSliceToOpsAction = new ForwardSliceToPCodeOpsAction(controller);
setGroupInfo(forwardSliceToOpsAction, highlightGroup, subGroupPosition++);
backwardSliceToOpsAction = new BackwardsSliceToPCodeOpsAction(controller);
setGroupInfo(backwardSliceToOpsAction, highlightGroup, subGroupPosition++);
tool.setMenuGroup(new String[] { "Secondary Highlight" }, highlightGroup);
setSecondaryHighlightAction = new SetSecondaryHighlightAction();
setGroupInfo(setSecondaryHighlightAction, highlightGroup, subGroupPosition++);
setSecondaryHighlightColorChooserAction = new SetSecondaryHighlightColorChooserAction();
setGroupInfo(setSecondaryHighlightColorChooserAction, highlightGroup, subGroupPosition++);
removeSecondaryHighlightAction = new RemoveSecondaryHighlightAction();
setGroupInfo(removeSecondaryHighlightAction, highlightGroup, subGroupPosition++);
removeAllSecondadryHighlightsAction = new RemoveAllSecondaryHighlightsAction();
setGroupInfo(removeAllSecondadryHighlightsAction, highlightGroup, subGroupPosition++);
//
// Comments
//
// NOTE: this is just a placeholder to represent where the comment actions should appear
// in relation to our local actions. We cannot control the comment action
// arrangement by setting the values, we can
// in relation to our local actions.
//
//
// Search
//
String searchGroup = "comment2 - Search Group";
subGroupPosition = 0; // reset for the next group
@ -835,6 +902,10 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
addLocalAction(lockProtoAction);
addLocalAction(lockLocalAction);
addLocalAction(renameVarAction);
addLocalAction(setSecondaryHighlightAction);
addLocalAction(setSecondaryHighlightColorChooserAction);
addLocalAction(removeSecondaryHighlightAction);
addLocalAction(removeAllSecondadryHighlightsAction);
addLocalAction(retypeVarAction);
addLocalAction(decompilerCreateStructureAction);
tool.addAction(listingCreateStructureAction);
@ -921,16 +992,6 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
}
@Override
public LayoutModel getLayoutModel() {
return controller.getDecompilerPanel().getLayoutModel();
}
@Override
public void clearHighlights() {
controller.getDecompilerPanel().clearHighlights();
}
public void programClosed(Program closedProgram) {
controller.programClosed(closedProgram);
}

View file

@ -0,0 +1,48 @@
/* ###
* 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.plugin.core.decompile.actions;
import ghidra.app.decompiler.ClangToken;
import ghidra.app.decompiler.component.DecompilerPanel;
import ghidra.app.decompiler.component.TokenHighlights;
import ghidra.app.plugin.core.decompile.DecompilerActionContext;
public abstract class AbstractSetSecondaryHighlightAction extends AbstractDecompilerAction {
AbstractSetSecondaryHighlightAction(String name) {
super(name);
}
@Override
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
if (!context.hasRealFunction()) {
return false;
}
DecompilerPanel panel = context.getDecompilerPanel();
ClangToken tokenAtCursor = panel.getTokenAtCursor();
if (tokenAtCursor == null) {
return false;
}
TokenHighlights highlightedTokens = panel.getSecondaryHighlightedTokens();
if (highlightedTokens.contains(tokenAtCursor)) {
return false; // already highlighted
}
return true;
}
}

View file

@ -19,23 +19,24 @@ import java.util.Set;
import docking.action.MenuData;
import ghidra.app.decompiler.ClangToken;
import ghidra.app.decompiler.component.*;
import ghidra.app.decompiler.component.DecompilerPanel;
import ghidra.app.decompiler.component.DecompilerUtils;
import ghidra.app.plugin.core.decompile.DecompilerActionContext;
import ghidra.program.model.pcode.PcodeOp;
import ghidra.program.model.pcode.Varnode;
public class BackwardsSliceAction extends AbstractDecompilerAction {
private final DecompilerController controller;
public BackwardsSliceAction(DecompilerController controller) {
public static final String NAME = "Highlight Backward Slice";
public BackwardsSliceAction() {
super("Highlight Backward Slice");
this.controller = controller;
setPopupMenuData(new MenuData(new String[] { "Highlight Backward Slice" }, "Decompile"));
setPopupMenuData(new MenuData(new String[] { "Highlight", "Backward Slice" }, "Decompile"));
}
@Override
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
DecompilerPanel decompilerPanel = controller.getDecompilerPanel();
DecompilerPanel decompilerPanel = context.getDecompilerPanel();
ClangToken tokenAtCursor = decompilerPanel.getTokenAtCursor();
Varnode varnode = DecompilerUtils.getVarnodeRef(tokenAtCursor);
return varnode != null;
@ -43,18 +44,21 @@ public class BackwardsSliceAction extends AbstractDecompilerAction {
@Override
protected void decompilerActionPerformed(DecompilerActionContext context) {
DecompilerPanel decompilerPanel = controller.getDecompilerPanel();
DecompilerPanel decompilerPanel = context.getDecompilerPanel();
ClangToken tokenAtCursor = decompilerPanel.getTokenAtCursor();
Varnode varnode = DecompilerUtils.getVarnodeRef(tokenAtCursor);
if (varnode != null) {
PcodeOp op = tokenAtCursor.getPcodeOp();
Set<Varnode> backwardSlice = DecompilerUtils.getBackwardSlice(varnode);
decompilerPanel.clearHighlights();
decompilerPanel.addVarnodeHighlights(backwardSlice,
decompilerPanel.getDefaultHighlightColor(), varnode, op,
decompilerPanel.getDefaultSpecialColor());
decompilerPanel.repaint();
if (varnode == null) {
return;
}
decompilerPanel.clearPrimaryHighlights();
PcodeOp op = tokenAtCursor.getPcodeOp();
Set<Varnode> backwardSlice = DecompilerUtils.getBackwardSlice(varnode);
SliceHighlightColorProvider colorProvider =
new SliceHighlightColorProvider(decompilerPanel, backwardSlice, varnode, op);
decompilerPanel.addVarnodeHighlights(backwardSlice, colorProvider);
}
}

View file

@ -31,7 +31,7 @@ public class BackwardsSliceToPCodeOpsAction extends AbstractDecompilerAction {
super("Highlight Backward Inst Slice");
this.controller = controller;
setPopupMenuData(
new MenuData(new String[] { "Highlight Backward Inst Slice" }, "Decompile"));
new MenuData(new String[] { "Highlight", "Backward Inst Slice" }, "Decompile"));
}
@Override
@ -51,10 +51,9 @@ public class BackwardsSliceToPCodeOpsAction extends AbstractDecompilerAction {
PcodeOp op = tokenAtCursor.getPcodeOp();
Set<PcodeOp> backwardSlice = DecompilerUtils.getBackwardSliceToPCodeOps(varnode);
backwardSlice.add(op);
decompilerPanel.clearHighlights();
decompilerPanel.clearPrimaryHighlights();
decompilerPanel.addPcodeOpHighlights(backwardSlice,
decompilerPanel.getDefaultHighlightColor());
decompilerPanel.repaint();
decompilerPanel.getCurrentVariableHighlightColor());
}
}

View file

@ -22,12 +22,10 @@ import ghidra.app.decompiler.component.DecompilerController;
import ghidra.app.decompiler.component.DecompilerPanel;
import ghidra.app.plugin.core.decompile.DecompilerActionContext;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Program;
import ghidra.program.model.pcode.HighFunction;
import ghidra.program.model.pcode.HighFunctionDBUtil;
import ghidra.program.model.symbol.SourceType;
import ghidra.util.UndefinedFunction;
public class CommitLocalsAction extends AbstractDecompilerAction {
private final DecompilerController controller;
@ -55,8 +53,7 @@ public class CommitLocalsAction extends AbstractDecompilerAction {
@Override
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
Function function = controller.getFunction();
if (function == null || function instanceof UndefinedFunction) {
if (!context.hasRealFunction()) {
return false;
}
return getHighFunction() != null;

View file

@ -25,13 +25,11 @@ import ghidra.app.decompiler.component.DecompilerController;
import ghidra.app.decompiler.component.DecompilerPanel;
import ghidra.app.plugin.core.decompile.DecompilerActionContext;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Program;
import ghidra.program.model.pcode.HighFunction;
import ghidra.program.model.pcode.HighFunctionDBUtil;
import ghidra.program.model.symbol.SourceType;
import ghidra.util.Msg;
import ghidra.util.UndefinedFunction;
import ghidra.util.exception.*;
public class CommitParamsAction extends AbstractDecompilerAction {
@ -65,9 +63,7 @@ public class CommitParamsAction extends AbstractDecompilerAction {
@Override
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
Function function = controller.getFunction();
if (function == null || function instanceof UndefinedFunction) {
if (!context.hasRealFunction()) {
return false;
}
return getHighFunction() != null;

View file

@ -30,7 +30,7 @@ public class ForwardSliceAction extends AbstractDecompilerAction {
public ForwardSliceAction(DecompilerController controller) {
super("Highlight Forward Slice");
this.controller = controller;
setPopupMenuData(new MenuData(new String[] { "Highlight Forward Slice" }, "Decompile"));
setPopupMenuData(new MenuData(new String[] { "Highlight", "Forward Slice" }, "Decompile"));
}
@Override
@ -46,15 +46,17 @@ public class ForwardSliceAction extends AbstractDecompilerAction {
DecompilerPanel decompilerPanel = controller.getDecompilerPanel();
ClangToken tokenAtCursor = decompilerPanel.getTokenAtCursor();
Varnode varnode = DecompilerUtils.getVarnodeRef(tokenAtCursor);
if (varnode != null) {
PcodeOp op = tokenAtCursor.getPcodeOp();
Set<Varnode> forwardSlice = DecompilerUtils.getForwardSlice(varnode);
decompilerPanel.clearHighlights();
decompilerPanel.addVarnodeHighlights(forwardSlice,
decompilerPanel.getDefaultHighlightColor(), varnode, op,
decompilerPanel.getDefaultSpecialColor());
decompilerPanel.repaint();
if (varnode == null) {
return;
}
PcodeOp op = tokenAtCursor.getPcodeOp();
Set<Varnode> forwardSlice = DecompilerUtils.getForwardSlice(varnode);
decompilerPanel.clearPrimaryHighlights();
SliceHighlightColorProvider colorProvider =
new SliceHighlightColorProvider(decompilerPanel, forwardSlice, varnode, op);
decompilerPanel.addVarnodeHighlights(forwardSlice, colorProvider);
}
}

View file

@ -31,7 +31,7 @@ public class ForwardSliceToPCodeOpsAction extends AbstractDecompilerAction {
super("Highlight Forward Inst Slice");
this.controller = controller;
setPopupMenuData(
new MenuData(new String[] { "Highlight Forward Inst Slice" }, "Decompile"));
new MenuData(new String[] { "Highlight", "Forward Inst Slice" }, "Decompile"));
}
@Override
@ -51,10 +51,9 @@ public class ForwardSliceToPCodeOpsAction extends AbstractDecompilerAction {
PcodeOp op = tokenAtCursor.getPcodeOp();
Set<PcodeOp> forwardSlice = DecompilerUtils.getForwardSliceToPCodeOps(varnode);
forwardSlice.add(op);
decompilerPanel.clearHighlights();
decompilerPanel.clearPrimaryHighlights();
decompilerPanel.addPcodeOpHighlights(forwardSlice,
decompilerPanel.getDefaultHighlightColor());
decompilerPanel.repaint();
decompilerPanel.getCurrentVariableHighlightColor());
}
}

View file

@ -15,12 +15,13 @@
*/
package ghidra.app.plugin.core.decompile.actions;
import java.util.HashSet;
import java.util.Set;
import docking.action.MenuData;
import ghidra.app.decompiler.ClangToken;
import ghidra.app.decompiler.component.*;
import ghidra.app.plugin.core.decompile.DecompilerActionContext;
import ghidra.program.model.pcode.PcodeOp;
import ghidra.program.model.pcode.Varnode;
public class HighlightDefinedUseAction extends AbstractDecompilerAction {
@ -29,7 +30,7 @@ public class HighlightDefinedUseAction extends AbstractDecompilerAction {
public HighlightDefinedUseAction(DecompilerController controller) {
super("Highlight Defined Use");
this.controller = controller;
setPopupMenuData(new MenuData(new String[] { "Highlight Def-use" }, "Decompile"));
setPopupMenuData(new MenuData(new String[] { "Highlight", "Def-use" }, "Decompile"));
}
@Override
@ -45,15 +46,17 @@ public class HighlightDefinedUseAction extends AbstractDecompilerAction {
DecompilerPanel decompilerPanel = controller.getDecompilerPanel();
ClangToken tokenAtCursor = decompilerPanel.getTokenAtCursor();
Varnode varnode = DecompilerUtils.getVarnodeRef(tokenAtCursor);
if (varnode != null) {
HashSet<Varnode> varnodes = new HashSet<Varnode>();
varnodes.add(varnode);
decompilerPanel.clearHighlights();
decompilerPanel.addVarnodeHighlights(varnodes,
decompilerPanel.getDefaultHighlightColor(), varnode, varnode.getDef(),
decompilerPanel.getDefaultSpecialColor());
decompilerPanel.repaint();
if (varnode == null) {
return;
}
decompilerPanel.clearPrimaryHighlights();
Set<Varnode> varnodes = Set.of(varnode);
PcodeOp op = varnode.getDef();
SliceHighlightColorProvider colorProvider =
new SliceHighlightColorProvider(decompilerPanel, varnodes, varnode, op);
decompilerPanel.addVarnodeHighlights(varnodes, colorProvider);
}
}

View file

@ -0,0 +1,57 @@
/* ###
* 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.plugin.core.decompile.actions;
import docking.action.MenuData;
import ghidra.app.decompiler.component.*;
import ghidra.app.plugin.core.decompile.DecompilerActionContext;
import ghidra.app.util.HelpTopics;
import ghidra.util.HelpLocation;
/**
* Removes all secondary highlights for the current function
*
* @see ClangHighlightController
*/
public class RemoveAllSecondaryHighlightsAction extends AbstractDecompilerAction {
public static final String NAME = "Remove All Secondary Highlights";
public RemoveAllSecondaryHighlightsAction() {
super(NAME);
setPopupMenuData(new MenuData(
new String[] { "Secondary Highlight", "Remove All Highlights" }, "Decompile"));
setHelpLocation(new HelpLocation(HelpTopics.SELECTION, getName()));
}
@Override
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
if (!context.hasRealFunction()) {
return false;
}
DecompilerPanel panel = context.getDecompilerPanel();
TokenHighlights highlightedTokens = panel.getSecondaryHighlightedTokens();
return !highlightedTokens.isEmpty();
}
@Override
protected void decompilerActionPerformed(DecompilerActionContext context) {
DecompilerPanel panel = context.getDecompilerPanel();
panel.removeSecondaryHighlights();
}
}

View file

@ -0,0 +1,64 @@
/* ###
* 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.plugin.core.decompile.actions;
import docking.action.MenuData;
import ghidra.app.decompiler.ClangToken;
import ghidra.app.decompiler.component.*;
import ghidra.app.plugin.core.decompile.DecompilerActionContext;
import ghidra.app.util.HelpTopics;
import ghidra.util.HelpLocation;
/**
* Removes the selected token's secondary highlight
*
* @see ClangHighlightController
*/
public class RemoveSecondaryHighlightAction extends AbstractDecompilerAction {
public static final String NAME = "Remove Secondary Highlight";
public RemoveSecondaryHighlightAction() {
super(NAME);
setPopupMenuData(
new MenuData(new String[] { "Secondary Highlight", "Remove Highlight" }, "Decompile"));
setHelpLocation(new HelpLocation(HelpTopics.SELECTION, getName()));
}
@Override
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
if (!context.hasRealFunction()) {
return false;
}
DecompilerPanel panel = context.getDecompilerPanel();
ClangToken token = panel.getTokenAtCursor();
if (token == null) {
return false;
}
TokenHighlights highlightedTokens = panel.getSecondaryHighlightedTokens();
return highlightedTokens.contains(token);
}
@Override
protected void decompilerActionPerformed(DecompilerActionContext context) {
DecompilerPanel panel = context.getDecompilerPanel();
ClangToken token = panel.getTokenAtCursor();
panel.removeSecondaryHighlight(token);
}
}

View file

@ -259,6 +259,7 @@ public class RenameVariableAction extends AbstractDecompilerAction {
}
finally {
program.endTransaction(transaction, commit);
controller.getDecompilerPanel().tokenRenamed(tokenAtCursor, nameTask.getNewName());
}
}
}

View file

@ -0,0 +1,47 @@
/* ###
* 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.plugin.core.decompile.actions;
import docking.action.MenuData;
import ghidra.app.decompiler.ClangToken;
import ghidra.app.decompiler.component.ClangHighlightController;
import ghidra.app.decompiler.component.DecompilerPanel;
import ghidra.app.plugin.core.decompile.DecompilerActionContext;
/**
* Sets the secondary highlight on the selected token
*
* @see ClangHighlightController
*/
public class SetSecondaryHighlightAction extends AbstractSetSecondaryHighlightAction {
public static String NAME = "Set Secondary Highlight";
public SetSecondaryHighlightAction() {
super(NAME);
setPopupMenuData(
new MenuData(new String[] { "Secondary Highlight", "Set Highlight" }, "Decompile"));
}
@Override
protected void decompilerActionPerformed(DecompilerActionContext context) {
DecompilerPanel panel = context.getDecompilerPanel();
ClangToken token = panel.getTokenAtCursor();
panel.addSecondaryHighlight(token);
}
}

View file

@ -0,0 +1,62 @@
/* ###
* 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.plugin.core.decompile.actions;
import java.awt.Color;
import java.util.List;
import docking.action.MenuData;
import docking.options.editor.GhidraColorChooser;
import ghidra.app.decompiler.ClangToken;
import ghidra.app.decompiler.component.DecompilerPanel;
import ghidra.app.decompiler.component.TokenHighlightColors;
import ghidra.app.plugin.core.decompile.DecompilerActionContext;
public class SetSecondaryHighlightColorChooserAction extends AbstractSetSecondaryHighlightAction {
public static String NAME = "Set Secondary Highlight With Color";
public SetSecondaryHighlightColorChooserAction() {
super(NAME);
setPopupMenuData(
new MenuData(new String[] { "Secondary Highlight", "Set Highlight..." }, "Decompile"));
}
@Override
protected void decompilerActionPerformed(DecompilerActionContext context) {
DecompilerPanel panel = context.getDecompilerPanel();
ClangToken token = panel.getTokenAtCursor();
TokenHighlightColors colors = panel.getSecondaryHighlightColors();
List<Color> recentColors = colors.getRecentColors();
String name = token.getText();
Color currentColor = colors.getColor(name);
GhidraColorChooser chooser = new GhidraColorChooser(currentColor);
chooser.setColorHistory(recentColors);
chooser.setActiveTab("RGB");
Color colorChoice = chooser.showDialog(null);
if (colorChoice == null) {
return; // cancelled
}
colors.setColor(name, colorChoice);
panel.addSecondaryHighlight(token, colorChoice);
}
}

View file

@ -0,0 +1,74 @@
/* ###
* 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.plugin.core.decompile.actions;
import java.awt.Color;
import java.util.Set;
import ghidra.app.decompiler.ClangToken;
import ghidra.app.decompiler.component.DecompilerPanel;
import ghidra.app.decompiler.component.DecompilerUtils;
import ghidra.program.model.pcode.PcodeOp;
import ghidra.program.model.pcode.Varnode;
/**
* A class to provider a color for highlight a variable using one of the 'slice' actions
*
* @see ForwardSliceAction
* @see BackwardsSliceAction
*/
public class SliceHighlightColorProvider implements TokenHighlightColorProvider {
private Set<Varnode> varnodes;
private Varnode specialVn;
private PcodeOp specialOp;
private Color hlColor;
private Color specialHlColor;
SliceHighlightColorProvider(DecompilerPanel panel, Set<Varnode> varnodes, Varnode specialVn,
PcodeOp specialOp) {
this.varnodes = varnodes;
this.specialVn = specialVn;
this.specialOp = specialOp;
hlColor = panel.getCurrentVariableHighlightColor();
specialHlColor = panel.getSpecialHighlightColor();
}
@Override
public Color getColor(ClangToken token) {
Varnode vn = DecompilerUtils.getVarnodeRef(token);
if (vn == null) {
return null;
}
Color c = null;
if (varnodes.contains(vn)) {
c = hlColor;
}
if (specialOp == null) {
return c;
}
// look for specific varnode to label with special color
if (vn == specialVn && token.getPcodeOp() == specialOp) {
c = specialHlColor;
}
return c;
}
}

View file

@ -13,14 +13,22 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.decompiler.component;
package ghidra.app.plugin.core.decompile.actions;
import docking.widgets.fieldpanel.LayoutModel;
import java.awt.Color;
public interface DecompilerHighlightService {
import ghidra.app.decompiler.ClangToken;
public LayoutModel getLayoutModel();
public void clearHighlights();
/**
* Provides highlight color for the given token
*/
public interface TokenHighlightColorProvider {
/**
* Returns a color for the given token
*
* @param token the token
* @return the color
*/
public Color getColor(ClangToken token);
}

View file

@ -69,7 +69,8 @@ public abstract class AbstractDecompilerTest extends AbstractProgramBasedTest {
}
protected void waitForDecompiler() {
waitForCondition(() -> !provider.isDecompiling());
waitForSwing();
waitForCondition(() -> !provider.isBusy());
waitForSwing();
}
@ -79,6 +80,7 @@ public abstract class AbstractDecompilerTest extends AbstractProgramBasedTest {
DecompilerPanel panel = provider.getDecompilerPanel();
FieldPanel fp = panel.getFieldPanel();
click(fp, 1, true);
waitForSwing();
}
protected void doubleClick() {
@ -89,7 +91,7 @@ public abstract class AbstractDecompilerTest extends AbstractProgramBasedTest {
}
protected FieldLocation loc(int lineNumber, int col) {
FieldLocation loc = new FieldLocation(lineNumber, 0, 0, col);
FieldLocation loc = new FieldLocation(lineNumber - 1, 0, 0, col);
return loc;
}
@ -97,20 +99,56 @@ public abstract class AbstractDecompilerTest extends AbstractProgramBasedTest {
DecompilerPanel panel = provider.getDecompilerPanel();
List<Field> fields = panel.getFields();
Field line = fields.get(lineNumber - 1); // 0-based
Field line = fields.get(lineNumber - 1); // -1 for 1-based line number
return (ClangTextField) line;
}
protected String getTokenText(FieldLocation loc) {
ClangTextField field = getFieldForLine(loc.getIndex().intValue());
// note: the index is 0-based; use getFieldForLine() when using 1-based line numbers
protected ClangTextField getFieldForIndex(int lineIndex) {
DecompilerPanel panel = provider.getDecompilerPanel();
List<Field> fields = panel.getFields();
Field line = fields.get(lineIndex);
return (ClangTextField) line;
}
protected ClangToken getToken(int line, int col) {
FieldLocation loc = loc(line, col);
ClangTextField field = getFieldForLine(line);
ClangToken token = field.getToken(loc);
return token;
}
protected ClangToken getToken(FieldLocation loc) {
int lineNumber = loc.getIndex().intValue() + 1; // 0-based
ClangTextField field = getFieldForLine(lineNumber);
ClangToken token = field.getToken(loc);
return token;
}
protected DecompilerPanel getDecompilerPanel() {
return provider.getDecompilerPanel();
}
/**
* Returns the token under the cursor
* @return the token under the cursor
*/
protected ClangToken getToken() {
DecompilerPanel panel = getDecompilerPanel();
FieldLocation loc = panel.getCursorPosition();
return getToken(loc);
}
protected String getTokenText(FieldLocation loc) {
ClangToken token = getToken(loc);
return token.getText();
}
protected void assertToken(String tokenText, int line, int... cols) {
for (int col : cols) {
FieldLocation loc = loc(line, col);
String text = getTokenText(loc);
ClangToken token = getToken(line, col);
String text = token.getText();
assertEquals(tokenText, text);
}
}

View file

@ -48,10 +48,10 @@ public class DecompilerFindReferencesToNestedStructureActionTest
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| }
13|
10| _call_structure_B(&a->b);
11| return;
12| }
13|
*/
decompile(CALL_STRUCTURE_A_ADDRESS);

View file

@ -15,14 +15,13 @@
*/
package docking.options.editor;
import java.awt.Color;
import java.awt.Component;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.*;
import java.util.List;
import javax.swing.JColorChooser;
import javax.swing.JDialog;
import javax.swing.*;
import javax.swing.colorchooser.AbstractColorChooserPanel;
public class GhidraColorChooser extends JColorChooser {
@ -31,6 +30,7 @@ public class GhidraColorChooser extends JColorChooser {
private String title = DEFAULT_TITLE;
private RecentColorCache recentColorCache = new RecentColorCache();
private String activeTabName;
public GhidraColorChooser() {
super();
@ -54,12 +54,24 @@ public class GhidraColorChooser extends JColorChooser {
return recentColorCache.getMRUColorList();
}
/**
* Sets the active tab of this chooser to be the given tab name, if it exists (the color chooser
* UI may be different, depending upon the current Look and Feel)
*
* @param tabName the tab name
*/
public void setActiveTab(String tabName) {
activeTabName = tabName;
}
@SuppressWarnings("deprecation")
public Color showDialog(Component centerOverComponent) {
maybeInstallSettableColorSwatchChooserPanel();
OKListener okListener = new OKListener();
JDialog dialog = createDialog(centerOverComponent, title, true, this, okListener, null);
doSetActiveTab(dialog);
dialog.show(); // blocks until user brings dialog down...
Color color = okListener.getColor();
if (color != null) {
@ -68,6 +80,48 @@ public class GhidraColorChooser extends JColorChooser {
return color; // null if the user cancels
}
private void doSetActiveTab(JDialog dialog) {
if (activeTabName == null) {
return;
}
JTabbedPane pane = findTabbedPane(dialog);
if (pane == null) {
return;
}
int n = pane.getTabCount();
for (int i = 0; i < n; i++) {
String tabTitle = pane.getTitleAt(i);
if (activeTabName.equals(tabTitle)) {
pane.setSelectedIndex(i);
return;
}
}
}
private JTabbedPane findTabbedPane(Component component) {
if (!(component instanceof Container)) {
return null;
}
Container parent = (Container) component;
if (parent instanceof JTabbedPane) {
return (JTabbedPane) parent;
}
int n = parent.getComponentCount();
for (int i = 0; i < n; i++) {
Component child = parent.getComponent(i);
JTabbedPane pane = findTabbedPane(child);
if (pane != null) {
return pane;
}
}
return null;
}
private void maybeInstallSettableColorSwatchChooserPanel() {
if (recentColorCache.size() == 0) {
return;
@ -87,7 +141,13 @@ public class GhidraColorChooser extends JColorChooser {
SettableColorSwatchChooserPanel newSwatchPanel =
new SettableColorSwatchChooserPanel(mruColorList);
AbstractColorChooserPanel[] newChooserPanels =
new AbstractColorChooserPanel[] { newSwatchPanel, chooserPanels[1], chooserPanels[2] };
new AbstractColorChooserPanel[chooserPanels.length];
newChooserPanels[0] = newSwatchPanel;
for (int i = 1; i < chooserPanels.length; i++) {
AbstractColorChooserPanel panel = chooserPanels[i];
newChooserPanels[i] = panel;
}
setChooserPanels(newChooserPanels);
}

View file

@ -56,21 +56,6 @@ public class InputDialog extends DialogComponentProvider {
this(dialogTitle, new String[] { label }, new String[] { DEFAULT_VALUE }, true, null);
}
/**
* Creates a generic input dialog with the specified title, a text field,
* labeled by the specified label. The user should check the value of
* "isCanceled()" to know whether or not the user canceled the operation.
* Otherwise, use the "getValue()" or "getValues()" to get the value(s)
* entered by the user. Use the tool's "showDialog()" to display the dialog.
* <P>
* @param dialogTitle used as the name of the dialog's title bar
* @param label value to use for the label of the text field
* @param listener listener that is called when the OK button is hit
*/
public InputDialog(String dialogTitle, String label, InputDialogListener listener) {
this(dialogTitle, new String[] { label }, new String[] { DEFAULT_VALUE }, true, listener);
}
/**
* Creates a generic input dialog with the specified title, a text field,
* labeled by the specified label. The user should check the value of
@ -96,27 +81,13 @@ public class InputDialog extends DialogComponentProvider {
* @param dialogTitle used as the name of the dialog's title bar
* @param label value to use for the label of the text field
* @param initialValue initial value to use for the text field
* @param listener the dialog listener (may be null)
*/
public InputDialog(String dialogTitle, String label, String initialValue,
InputDialogListener listener) {
this(dialogTitle, new String[] { label }, new String[] { initialValue }, true, listener);
}
/**
* Creates a generic input dialog with the specified title, a text field,
* labeled by the specified label. The user should check the value of
* "isCanceled()" to know whether or not the user canceled the operation.
* Otherwise, use the "getValue()" or "getValues()" to get the value(s)
* entered by the user. Use the tool's "showDialog()" to display the dialog.
* <P>
* @param dialogTitle used as the name of the dialog's title bar
* @param label values to use for the label of the text field
* @param isModal whether or not the dialog is to be modal
*/
public InputDialog(String dialogTitle, String label, boolean isModal) {
this(dialogTitle, new String[] { label }, new String[] { DEFAULT_VALUE }, isModal, null);
}
/**
* Creates a generic input dialog with the specified title, a text field,
* labeled by the specified label. The user should check the value of
@ -163,8 +134,8 @@ public class InputDialog extends DialogComponentProvider {
*/
public InputDialog(String dialogTitle, String[] labels, String[] initialValues, boolean isModal,
InputDialogListener listener) {
super(dialogTitle, isModal, (listener != null), // status area needed?
true, false); // do need button panel
super(dialogTitle, isModal, (listener != null) /* status */, true /* buttons */,
false /* no tasks */);
this.listener = listener;
// create the key listener all the text fields will use
@ -253,40 +224,36 @@ public class InputDialog extends DialogComponentProvider {
close();
}
//***************************************************************************
//* API methods
//**************************************************************************/
/**
* Returns if this dialog is cancelled.
* Returns if this dialog is cancelled
* @return true if cancelled
*/
public boolean isCanceled() {
return isCanceled;
}
/**
* return the value of the first (and maybe only) text field
* Return the value of the first (and maybe only) text field
* @return the text field value
*/
public String getValue() {
return inputValues[0];
}
/**
* return the values for all the text field(s)
* Sets the text of the primary text field
* @param text the text
*/
public String[] getValues() {
return inputValues;
public void setValue(String text) {
textFields[0].setText(text);
}
/**
* reset all the text fields to their initial values
* Return the values for all the text field(s)
* @return the text field values
*/
public void resetValues() {
for (int v = 0; v < inputValues.length; v++) {
String value = initialValues[v];
inputValues[v] = value;
textFields[v].setText(value);
}
public String[] getValues() {
return inputValues;
}
private class MyTextField extends JTextField {

View file

@ -1633,8 +1633,8 @@ public class FieldPanel extends JPanel
if (e.getButton() != MouseEvent.BUTTON1) {
return;
}
cursorHandler.setCursorPos(e.getX(), e.getY(), null);
cursorHandler.notifyCursorChanged(EventTrigger.GUI_ACTION);
cursorHandler.setCursorPos(e.getX(), e.getY(), EventTrigger.GUI_ACTION);
if (!selectionHandler.isInProgress() && !didDrag) {
selectionHandler.clearSelection();
}