mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 10:49:34 +02:00
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:
parent
ec9a644363
commit
0c305084a9
12 changed files with 453 additions and 99 deletions
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue