GT-3292 - Decompiler - Secondary Highlights - Checkpoint 4 - Tests

passing; all fixed bugs in color chooser; gave up on persistence for
now; still need help
This commit is contained in:
dragonmacher 2019-12-09 18:29:01 -05:00
parent ec9a644363
commit 0c305084a9
12 changed files with 453 additions and 99 deletions

View file

@ -83,6 +83,10 @@ public abstract class ClangHighlightController {
return null;
}
public TokenHighlightColors getSecondaryHighlightColors() {
return secondaryHighlightColors;
}
public TokenHighlights getPrimaryHighlightedTokens() {
return primaryHighlightTokens;
}
@ -164,22 +168,6 @@ public abstract class ClangHighlightController {
addPrimaryHighlights(tokens, hlColor);
}
public void toggleSecondaryMultiHighlight(ClangToken token,
Supplier<? extends Collection<ClangToken>> lazyTokens) {
String text = token.getText();
// toggle the highlight
HighlightToken highlight = secondaryHighlightTokens.get(token);
if (highlight == null) {
Color highlightColor = secondaryHighlightColors.getColor(text);
addSecondaryHighlights(lazyTokens, highlightColor);
}
else {
removeSecondaryHighlights(lazyTokens);
}
}
public boolean hasPrimaryHighlight(ClangToken token) {
return primaryHighlightTokens.contains(token);
}
@ -204,7 +192,7 @@ public abstract class ClangHighlightController {
return hlToken;
}
private void removeSecondaryHighlights(Supplier<? extends Collection<ClangToken>> tokens) {
public void removeSecondaryMultiHighlight(Supplier<? extends Collection<ClangToken>> tokens) {
for (ClangToken clangToken : tokens.get()) {
secondaryHighlightTokens.remove(clangToken);
updateHighlightColor(clangToken);
@ -212,7 +200,14 @@ public abstract class ClangHighlightController {
notifyListeners();
}
public void addSecondaryHighlights(Supplier<? extends Collection<ClangToken>> tokens,
public void addSecondaryMultiHighlight(ClangToken token,
Supplier<? extends Collection<ClangToken>> tokens) {
String text = token.getText();
Color highlightColor = secondaryHighlightColors.getColor(text);
addSecondaryMultiHighlight(tokens, highlightColor);
}
public void addSecondaryMultiHighlight(Supplier<? extends Collection<ClangToken>> tokens,
Color hlColor) {
Function<ClangToken, Color> colorProvider = token -> hlColor;
addTokensToHighlights(tokens.get(), colorProvider, secondaryHighlightTokens);

View file

@ -149,10 +149,14 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
String tokenName = entry.getKey();
Color color = entry.getValue();
Supplier<List<ClangToken>> lazyTokens = () -> findTokensByName(tokenName);
highlightController.addSecondaryHighlights(lazyTokens, color);
highlightController.addSecondaryMultiHighlight(lazyTokens, color);
}
}
public TokenHighlightColors getSecondaryHighlightColors() {
return highlightController.getSecondaryHighlightColors();
}
public TokenHighlights getSecondaryHighlightedTokens() {
return highlightController.getSecondaryHighlightedTokens();
}
@ -162,8 +166,19 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
highlightController.removeSecondaryHighlights(function);
}
public void toggleSecondaryHighlight(ClangToken token, Supplier<List<ClangToken>> lazyTokens) {
highlightController.toggleSecondaryMultiHighlight(token, lazyTokens);
public void removeSecondaryHighlight(ClangToken token) {
Supplier<List<ClangToken>> lazyTokens = () -> findTokensByName(token.getText());
highlightController.removeSecondaryMultiHighlight(lazyTokens);
}
public void addSecondaryHighlight(ClangToken token) {
Supplier<List<ClangToken>> lazyTokens = () -> findTokensByName(token.getText());
highlightController.addSecondaryMultiHighlight(token, lazyTokens);
}
public void addSecondaryHighlight(ClangToken token, Color color) {
Supplier<List<ClangToken>> lazyTokens = () -> findTokensByName(token.getText());
highlightController.addSecondaryMultiHighlight(lazyTokens, color);
}
private void togglePrimaryHighlight(FieldLocation location, Field field, Color highlightColor) {
@ -971,7 +986,7 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
Supplier<List<ClangToken>> lazyTokens = () -> findTokensByName(name);
Color color = hlToken.getColor();
highlightController.addSecondaryHighlights(lazyTokens, color);
highlightController.addSecondaryMultiHighlight(lazyTokens, color);
repaint();
});
}

View file

@ -16,8 +16,7 @@
package ghidra.app.decompiler.component;
import java.awt.Color;
import java.util.HashMap;
import java.util.Map;
import java.util.*;
/**
* A class to create and store colors related to token names
@ -27,6 +26,7 @@ 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)),
@ -38,4 +38,13 @@ public class TokenHighlightColors {
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

@ -67,6 +67,10 @@ public class TokenHighlights implements Iterable<HighlightToken> {
return highFunction.getFunction();
}
public boolean isEmpty() {
return size() == 0;
}
public int size() {
return highlightsByToken.size();
}

View file

@ -88,8 +88,10 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
private SelectAllAction selectAllAction;
private ToggleSecondaryHighlightAction setHighlightAction;
private RemoveSecondaryHighlightsAction removePanelHighlightsAction;
private SetSecondaryHighlightAction setSecondaryHighlightAction;
private SetSecondaryHighlightColorChooserAction setSecondaryHighlightColorChooserAction;
private RemoveAllSecondaryHighlightsAction removeAllSecondadryHighlightsAction;
private RemoveSecondaryHighlightAction removeSecondaryHighlightAction;
private final DecompilePlugin plugin;
private ClipboardService clipboardService;
@ -821,11 +823,17 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
//
// Highlight
//
setHighlightAction = new ToggleSecondaryHighlightAction();
setGroupInfo(setHighlightAction, highlightGroup, subGroupPosition++);
setSecondaryHighlightAction = new SetSecondaryHighlightAction();
setGroupInfo(setSecondaryHighlightAction, highlightGroup, subGroupPosition++);
removePanelHighlightsAction = new RemoveSecondaryHighlightsAction();
setGroupInfo(removePanelHighlightsAction, highlightGroup, subGroupPosition++);
setSecondaryHighlightColorChooserAction = new SetSecondaryHighlightColorChooserAction();
setGroupInfo(setSecondaryHighlightColorChooserAction, highlightGroup, subGroupPosition++);
removeSecondaryHighlightAction = new RemoveSecondaryHighlightAction();
setGroupInfo(removeSecondaryHighlightAction, highlightGroup, subGroupPosition++);
removeAllSecondadryHighlightsAction = new RemoveAllSecondaryHighlightsAction();
setGroupInfo(removeAllSecondadryHighlightsAction, highlightGroup, subGroupPosition++);
//
// Comments
@ -892,8 +900,10 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
addLocalAction(lockProtoAction);
addLocalAction(lockLocalAction);
addLocalAction(renameVarAction);
addLocalAction(setHighlightAction);
addLocalAction(removePanelHighlightsAction);
addLocalAction(setSecondaryHighlightAction);
addLocalAction(setSecondaryHighlightColorChooserAction);
addLocalAction(removeSecondaryHighlightAction);
addLocalAction(removeAllSecondadryHighlightsAction);
addLocalAction(retypeVarAction);
addLocalAction(decompilerCreateStructureAction);
tool.addAction(listingCreateStructureAction);

View file

@ -0,0 +1,53 @@
/* ###
* 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;
import ghidra.app.util.HelpTopics;
import ghidra.util.HelpLocation;
public abstract class AbstractSetSecondaryHighlightAction extends AbstractDecompilerAction {
AbstractSetSecondaryHighlightAction(String name) {
super(name);
// TODO new help
setHelpLocation(new HelpLocation(HelpTopics.SELECTION, getName()));
}
@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

@ -16,16 +16,21 @@
package ghidra.app.plugin.core.decompile.actions;
import docking.action.MenuData;
import ghidra.app.decompiler.component.DecompilerPanel;
import ghidra.app.decompiler.component.*;
import ghidra.app.plugin.core.decompile.DecompilerActionContext;
import ghidra.app.util.HelpTopics;
import ghidra.util.HelpLocation;
public class RemoveSecondaryHighlightsAction extends AbstractDecompilerAction {
/**
* Removes all secondary highlights for the current function
*
* @see ClangHighlightController
*/
public class RemoveAllSecondaryHighlightsAction extends AbstractDecompilerAction {
public static final String NAME = "Remove Secondary Highlights";
public static final String NAME = "Remove All Secondary Highlights";
public RemoveSecondaryHighlightsAction() {
public RemoveAllSecondaryHighlightsAction() {
super(NAME);
setPopupMenuData(new MenuData(new String[] { NAME }, "Decompile"));
@ -34,7 +39,13 @@ public class RemoveSecondaryHighlightsAction extends AbstractDecompilerAction {
@Override
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
return context.hasRealFunction();
if (!context.hasRealFunction()) {
return false;
}
DecompilerPanel panel = context.getDecompilerPanel();
TokenHighlights highlightedTokens = panel.getSecondaryHighlightedTokens();
return !highlightedTokens.isEmpty();
}
@Override

View file

@ -15,27 +15,26 @@
*/
package ghidra.app.plugin.core.decompile.actions;
import java.util.List;
import java.util.function.Supplier;
import docking.action.MenuData;
import ghidra.app.decompiler.ClangToken;
import ghidra.app.decompiler.component.DecompilerPanel;
import ghidra.app.decompiler.component.*;
import ghidra.app.plugin.core.decompile.DecompilerActionContext;
import ghidra.app.util.HelpTopics;
import ghidra.program.model.pcode.HighVariable;
import ghidra.util.HelpLocation;
public class ToggleSecondaryHighlightAction extends AbstractDecompilerAction {
/**
* Removes the selected token's secondary highlight
*
* @see ClangHighlightController
*/
public class RemoveSecondaryHighlightAction extends AbstractDecompilerAction {
public static String NAME = "Highlight Token";
public static final String NAME = "Remove Secondary Highlight";
public ToggleSecondaryHighlightAction() {
public RemoveSecondaryHighlightAction() {
super(NAME);
setPopupMenuData(new MenuData(new String[] { "Highlight Toggle" }, "Decompile"));
// TODO new help
setPopupMenuData(new MenuData(new String[] { NAME }, "Decompile"));
setHelpLocation(new HelpLocation(HelpTopics.SELECTION, getName()));
}
@ -46,31 +45,19 @@ public class ToggleSecondaryHighlightAction extends AbstractDecompilerAction {
}
DecompilerPanel panel = context.getDecompilerPanel();
ClangToken tokenAtCursor = panel.getTokenAtCursor();
if (tokenAtCursor == null) {
ClangToken token = panel.getTokenAtCursor();
if (token == null) {
return false;
}
//
// TODO Deal Breaker?? How to disable this action for Diff Decompilers??
//
// OR, just let it work
//
HighVariable variable = tokenAtCursor.getHighVariable();
// TODO does not work if behind a variable name; works in front
// TODO restrict to variable, or allow any text?
return variable != null;
TokenHighlights highlightedTokens = panel.getSecondaryHighlightedTokens();
return highlightedTokens.contains(token);
}
@Override
protected void decompilerActionPerformed(DecompilerActionContext context) {
DecompilerPanel panel = context.getDecompilerPanel();
ClangToken token = panel.getTokenAtCursor();
Supplier<List<ClangToken>> lazyTokens = () -> panel.findTokensByName(token.getText());
panel.toggleSecondaryHighlight(token, lazyTokens);
panel.removeSecondaryHighlight(token);
}
}

View file

@ -0,0 +1,46 @@
/* ###
* 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[] { NAME }, "Decompile"));
}
@Override
protected void decompilerActionPerformed(DecompilerActionContext context) {
DecompilerPanel panel = context.getDecompilerPanel();
ClangToken token = panel.getTokenAtCursor();
panel.addSecondaryHighlight(token);
}
}

View file

@ -0,0 +1,61 @@
/* ###
* 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[] { NAME + "..." }, "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

@ -18,14 +18,18 @@ package ghidra.app.plugin.core.decompile;
import static org.junit.Assert.*;
import java.awt.Color;
import java.awt.Window;
import java.awt.event.MouseEvent;
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import javax.swing.JButton;
import org.junit.Test;
import docking.action.DockingActionIf;
import docking.options.editor.GhidraColorChooser;
import docking.widgets.dialogs.InputDialog;
import docking.widgets.fieldpanel.FieldPanel;
import docking.widgets.fieldpanel.field.Field;
@ -304,7 +308,7 @@ public class DecompilerClangTest extends AbstractDecompilerTest {
assertEquals("_printf", text1);
Color color = highlight();
assertAllFieldsHighlightedExceptForToken(token1, color);
assertAllFieldsSecondaryHighlighted(token1, color);
// 5:30 "a->name"
line = 5;
@ -316,7 +320,7 @@ public class DecompilerClangTest extends AbstractDecompilerTest {
Color color2 = highlight();
assertAllFieldsHighlighted(text1, color);
assertAllFieldsHighlightedExceptForToken(token2, color2);
assertAllFieldsSecondaryHighlighted(token2, color2);
// 2:1 "void"
line = 2;
@ -329,7 +333,7 @@ public class DecompilerClangTest extends AbstractDecompilerTest {
Color color3 = highlight();
assertAllFieldsHighlighted(text1, color);
assertAllFieldsHighlighted(text2, color2);
assertAllFieldsHighlightedExceptForToken(token3, color3);
assertAllFieldsSecondaryHighlighted(token3, color3);
}
@Test
@ -381,7 +385,7 @@ public class DecompilerClangTest extends AbstractDecompilerTest {
line = 5;
charPosition = 2;
setDecompilerLocation(line, charPosition);
clearSecondaryHighlight();
removeSecondaryHighlight();
assertNoFieldsSecondaryHighlighted(text);
assertAllFieldsHighlighted(text2, color2);
@ -478,7 +482,7 @@ public class DecompilerClangTest extends AbstractDecompilerTest {
token = getToken();
text = token.getText();
assertEquals("bob", text);
assertAllFieldsHighlightedExceptForToken(token, color);
assertAllFieldsSecondaryHighlighted(token, color);
}
@Test
@ -516,7 +520,7 @@ public class DecompilerClangTest extends AbstractDecompilerTest {
Color color = highlight();
clearSecondaryHighlight();
removeSecondaryHighlight();
assertNoFieldsSecondaryHighlighted(text);
@ -558,7 +562,7 @@ public class DecompilerClangTest extends AbstractDecompilerTest {
assertEquals("_printf", text);
Color color = highlight();
assertAllFieldsHighlightedExceptForToken(token, color);
assertAllFieldsSecondaryHighlighted(token, color);
setDecompilerLocation(line, charPosition + 1);
assertCombinedHighlightColor(token);
@ -664,7 +668,7 @@ public class DecompilerClangTest extends AbstractDecompilerTest {
token = getToken();
assertCombinedHighlightColor(token);
assertAllFieldsHighlightedExceptForToken(token, color);
assertAllFieldsSecondaryHighlighted(token, color);
// no click away and make sure the secondary highlight color returns
// 10:19 "&a"
@ -754,19 +758,87 @@ public class DecompilerClangTest extends AbstractDecompilerTest {
assertAllFieldsHighlighted(clone, secondaryHighlightText, color);
// ensure one field provider does not affect the other
clearSecondaryHighlight();
removeSecondaryHighlight();
assertNoFieldsSecondaryHighlighted(secondaryHighlightText);
assertAllFieldsHighlighted(clone, secondaryHighlightText, color);
}
@Test
public void testSecondaryHighlighting_ChooseColors() {
fail();
/*
Decomp of '_call_structure_A':
1|
2| void _call_structure_A(A *a)
3|
4| {
5| _printf("call_structure_A: %s\n",a->name);
6| _printf("call_structure_A: %s\n",(a->b).name);
7| _printf("call_structure_A: %s\n",(a->b).c.name);
8| _printf("call_structure_A: %s\n",(a->b).c.d.name);
9| _printf("call_structure_A: %s\n",(a->b).c.d.e.name);
10| _call_structure_B(&a->b);
11| return;
12| }
*/
decompile("100000d60"); // '_call_structure_A'
// 5:2 "_printf"
int line = 5;
int charPosition = 2;
setDecompilerLocation(line, charPosition);
ClangToken token = getToken();
String secondaryHighlightText = token.getText();
assertEquals("_printf", secondaryHighlightText);
Color myColor = Color.PINK;
highlightWithColorChooser(myColor);
assertAllFieldsSecondaryHighlighted(token, myColor);
}
@Test
public void testSecondaryHighlighting_PersistColors() {
fail();
public void testSecondaryHighlighting_ChooseColors_ColorIsLaterReusedForSameToken() {
/*
Decomp of '_call_structure_A':
1|
2| void _call_structure_A(A *a)
3|
4| {
5| _printf("call_structure_A: %s\n",a->name);
6| _printf("call_structure_A: %s\n",(a->b).name);
7| _printf("call_structure_A: %s\n",(a->b).c.name);
8| _printf("call_structure_A: %s\n",(a->b).c.d.name);
9| _printf("call_structure_A: %s\n",(a->b).c.d.e.name);
10| _call_structure_B(&a->b);
11| return;
12| }
*/
decompile("100000d60"); // '_call_structure_A'
// 5:2 "_printf"
int line = 5;
int charPosition = 2;
setDecompilerLocation(line, charPosition);
ClangToken token = getToken();
String secondaryHighlightText = token.getText();
assertEquals("_printf", secondaryHighlightText);
Color myColor = Color.PINK;
highlightWithColorChooser(myColor);
removeSecondaryHighlight();
Color hlColor2 = highlight();
assertEquals(myColor, hlColor2);
}
//==================================================================================================
@ -897,7 +969,7 @@ public class DecompilerClangTest extends AbstractDecompilerTest {
private void clearAllHighlights() {
DockingActionIf highlightAction =
getAction(decompiler, RemoveSecondaryHighlightsAction.NAME);
getAction(decompiler, RemoveAllSecondaryHighlightsAction.NAME);
performAction(highlightAction);
}
@ -905,31 +977,55 @@ public class DecompilerClangTest extends AbstractDecompilerTest {
ClangToken token = getToken();
DockingActionIf highlightAction =
getAction(decompiler, ToggleSecondaryHighlightAction.NAME);
DockingActionIf highlightAction = getAction(decompiler, SetSecondaryHighlightAction.NAME);
performAction(highlightAction);
DecompilerController controller = provider.getController();
DecompilerPanel panel = controller.getDecompilerPanel();
TokenHighlights highlights = panel.getSecondaryHighlightedTokens();
HighlightToken ht = highlights.get(token);
HighlightToken ht = getSecondaryHighlight(token);
assertNotNull("No highlight for token: " + token, ht);
return ht.getColor();
}
private void clearSecondaryHighlight() {
ClangToken token = getToken();
DockingActionIf highlightAction =
getLocalAction(provider, ToggleSecondaryHighlightAction.NAME);
performAction(highlightAction, provider.getActionContext(null), true);
private HighlightToken getSecondaryHighlight(ClangToken token) {
DecompilerController controller = provider.getController();
DecompilerPanel panel = controller.getDecompilerPanel();
TokenHighlights highlights = panel.getSecondaryHighlightedTokens();
HighlightToken ht = highlights.get(token);
assertNull("Token should not be highlighted: " + token, ht);
return ht;
}
private void highlightWithColorChooser(Color color) {
ClangToken token = getToken();
DockingActionIf highlightAction =
getAction(decompiler, SetSecondaryHighlightColorChooserAction.NAME);
performAction(highlightAction, false);
Window w = waitForWindow("Please Choose a Color");
GhidraColorChooser colorChooser = findComponent(w, GhidraColorChooser.class);
JButton okButton = findButtonByText(w, "OK");
runSwing(() -> {
colorChooser.setColor(color);
okButton.doClick();
});
waitForSwing();
HighlightToken ht = getSecondaryHighlight(token);
assertNotNull("No highlight for token: " + token, ht);
Color hlColor = ht.getColor();
assertEquals(color, hlColor);
}
private void removeSecondaryHighlight() {
ClangToken token = getToken();
DockingActionIf highlightAction =
getLocalAction(provider, RemoveSecondaryHighlightAction.NAME);
performAction(highlightAction, provider.getActionContext(null), true);
HighlightToken ht = getSecondaryHighlight(token);
assertNull("Token should not be highlighted - '" + token + "': ", ht);
}
private void assertAllFieldsPrimaryHighlighted(String name) {
@ -942,10 +1038,17 @@ public class DecompilerClangTest extends AbstractDecompilerTest {
assertAllFieldsHighlighted(name, cm, noIgnores);
}
private void assertAllFieldsHighlightedExceptForToken(ClangToken token, Color color) {
private void assertAllFieldsSecondaryHighlighted(ClangToken token, Color color) {
Predicate<ClangToken> ignores = t -> t == token;
String name = token.getText();
assertAllFieldsHighlighted(name, color, ignores);
// test the token under the cursor directly, as that may have a combined highlight applied
Color combinedColor = getCombinedHighlightColor(token);
ColorMatcher cm = new ColorMatcher(color, combinedColor);
Color actual = token.getHighlight();
assertTrue("Token is not highlighted: '" + token + "'" + "\n\texpected: " + cm +
"; found: " + toString(actual), cm.matches(actual));
}
private void assertNoFieldsSecondaryHighlighted(String name) {

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);
}