diff --git a/Ghidra/Extensions/SampleTablePlugin/certification.manifest b/Ghidra/Extensions/SampleTablePlugin/certification.manifest index b9b2c11b56..c982dd70ad 100644 --- a/Ghidra/Extensions/SampleTablePlugin/certification.manifest +++ b/Ghidra/Extensions/SampleTablePlugin/certification.manifest @@ -3,6 +3,7 @@ ##MODULE IP: Oxygen Icons - LGPL 3.0 Module.manifest||GHIDRA||reviewed||END| data/ExtensionPoint.manifest||GHIDRA||||END| +data/sample.plugin.theme.properties||GHIDRA||||END| extension.properties||GHIDRA||||END| src/main/help/help/TOC_Source.xml||GHIDRA||||END| src/main/help/help/shared/arrow.gif||GHIDRA||reviewed||END| diff --git a/Ghidra/Extensions/SampleTablePlugin/data/sample.plugin.theme.properties b/Ghidra/Extensions/SampleTablePlugin/data/sample.plugin.theme.properties new file mode 100644 index 0000000000..b001219c91 --- /dev/null +++ b/Ghidra/Extensions/SampleTablePlugin/data/sample.plugin.theme.properties @@ -0,0 +1,3 @@ +[Defaults] +icon.sample.plugin.action.show.options = table.png +icon.sample.plugin.action.save = disk.png \ No newline at end of file diff --git a/Ghidra/Extensions/SampleTablePlugin/src/main/java/ghidra/examples/SampleTableProvider.java b/Ghidra/Extensions/SampleTablePlugin/src/main/java/ghidra/examples/SampleTableProvider.java index 6d071a29a7..530d0f762b 100644 --- a/Ghidra/Extensions/SampleTablePlugin/src/main/java/ghidra/examples/SampleTableProvider.java +++ b/Ghidra/Extensions/SampleTablePlugin/src/main/java/ghidra/examples/SampleTableProvider.java @@ -27,6 +27,7 @@ import docking.action.*; import docking.widgets.checkbox.GCheckBox; import docking.widgets.filechooser.GhidraFileChooserPanel; import docking.widgets.table.GFilterTable; +import generic.theme.GIcon; import ghidra.framework.options.OptionsChangeListener; import ghidra.framework.options.ToolOptions; import ghidra.framework.plugintool.ComponentProviderAdapter; @@ -34,7 +35,6 @@ import ghidra.framework.plugintool.util.OptionsService; import ghidra.util.*; import ghidra.util.classfinder.ClassSearcher; import ghidra.util.layout.MiddleLayout; -import resources.ResourceManager; public class SampleTableProvider extends ComponentProviderAdapter implements OptionsChangeListener { @@ -154,7 +154,7 @@ public class SampleTableProvider extends ComponentProviderAdapter implements Opt } }; - ImageIcon icon = ResourceManager.loadImage("images/table.png"); + Icon icon = new GIcon("icon.sample.plugin.action.show.options"); optionsAction.setToolBarData(new ToolBarData(icon)); DockingAction saveTableDataAction = new DockingAction("Save Table Data", plugin.getName()) { @@ -189,7 +189,7 @@ public class SampleTableProvider extends ComponentProviderAdapter implements Opt return SwingUtilities.isDescendingFrom((Component) sourceObject, filterTable); } }; - icon = ResourceManager.loadImage("images/disk.png"); + icon = new GIcon("icons.sample.plugin.action.save"); saveTableDataAction.setToolBarData(new ToolBarData(icon)); saveTableDataAction.setPopupMenuData(new MenuData(new String[] { "Save Data" })); diff --git a/Ghidra/Extensions/sample/certification.manifest b/Ghidra/Extensions/sample/certification.manifest index f76dd9c0ce..0f1a49b26c 100644 --- a/Ghidra/Extensions/sample/certification.manifest +++ b/Ghidra/Extensions/sample/certification.manifest @@ -3,6 +3,7 @@ ##MODULE IP: Oxygen Icons - LGPL 3.0 Module.manifest||GHIDRA||reviewed||END| data/README.txt||GHIDRA||||END| +data/sample.theme.properties||GHIDRA||||END| extension.properties||GHIDRA||||END| src/main/help/help/TOC_Source.xml||GHIDRA||||END| src/main/help/help/shared/arrow.gif||GHIDRA||reviewed||END| diff --git a/Ghidra/Extensions/sample/data/sample.theme.properties b/Ghidra/Extensions/sample/data/sample.theme.properties new file mode 100644 index 0000000000..02d966ddc8 --- /dev/null +++ b/Ghidra/Extensions/sample/data/sample.theme.properties @@ -0,0 +1,13 @@ +[Defaults] +icon.sample.provider = erase16.png +icon.sample.provider.graph = color_swatch.png +icon.sample.provider.hello.world = information.png + +icon.sample.action.hello.world = information.png +icon.sample.kitchen.sink.action.hellow.world = left.png +icon.sample.kitchen.sink.action.hellow.program = right.png + +icon.sample.action.show.graph = applications-development.png + +icon.sample.graph.dependency.layout = color_swatch.png +icon.sample.hello.world.action.clear = erase16.png \ No newline at end of file diff --git a/Ghidra/Extensions/sample/src/main/java/ghidra/examples/HelloWorldComponentProvider.java b/Ghidra/Extensions/sample/src/main/java/ghidra/examples/HelloWorldComponentProvider.java index 415543874b..b3c8d19ac5 100644 --- a/Ghidra/Extensions/sample/src/main/java/ghidra/examples/HelloWorldComponentProvider.java +++ b/Ghidra/Extensions/sample/src/main/java/ghidra/examples/HelloWorldComponentProvider.java @@ -24,14 +24,13 @@ import docking.ActionContext; import docking.WindowPosition; import docking.action.*; import docking.widgets.EmptyBorderButton; +import generic.theme.GIcon; import ghidra.framework.plugintool.ComponentProviderAdapter; import ghidra.framework.plugintool.PluginTool; import ghidra.util.HelpLocation; import ghidra.util.Msg; -import resources.ResourceManager; public class HelloWorldComponentProvider extends ComponentProviderAdapter { - private final static String PREV_IMAGE = "images/information.png"; private final static HelpLocation HELP = new HelpLocation("SampleHelpTopic", "SampleHelpTopic_Anchor_Name"); private MyButton activeButtonObj; @@ -41,7 +40,7 @@ public class HelloWorldComponentProvider extends ComponentProviderAdapter { public HelloWorldComponentProvider(PluginTool tool, String name) { super(tool, name, name); buildMainPanel(); - setIcon(ResourceManager.loadImage("images/erase16.png")); + setIcon(new GIcon("icon.sample.provider")); setHelpLocation(HELP); setDefaultWindowPosition(WindowPosition.WINDOW); setTitle("Hello World Component"); @@ -64,13 +63,13 @@ public class HelloWorldComponentProvider extends ComponentProviderAdapter { // put in Menu called "Hello", with Menu item of "World". Since this will be a local action // the menu item will appear on the local toolbar drop down. - ImageIcon prevImage = ResourceManager.loadImage(PREV_IMAGE); - action.setMenuBarData(new MenuData(new String[] { "Misc", "Hello World" }, prevImage)); + Icon icon = new GIcon("icon.sample.action.hello.world"); + action.setMenuBarData(new MenuData(new String[] { "Misc", "Hello World" }, icon)); action.setKeyBindingData(new KeyBindingData(KeyStroke.getKeyStroke(KeyEvent.VK_W, InputEvent.CTRL_MASK))); // puts the action on the local toolbar. - action.setToolBarData(new ToolBarData(prevImage)); + action.setToolBarData(new ToolBarData(icon)); action.setDescription("Hello World"); // set the help URL for the action diff --git a/Ghidra/Extensions/sample/src/main/java/ghidra/examples/KitchenSinkPlugin.java b/Ghidra/Extensions/sample/src/main/java/ghidra/examples/KitchenSinkPlugin.java index dcfd61bd58..36ab7f00e7 100644 --- a/Ghidra/Extensions/sample/src/main/java/ghidra/examples/KitchenSinkPlugin.java +++ b/Ghidra/Extensions/sample/src/main/java/ghidra/examples/KitchenSinkPlugin.java @@ -15,6 +15,14 @@ */ package ghidra.examples; +import java.awt.Event; +import java.awt.event.KeyEvent; + +import javax.swing.*; + +import docking.ActionContext; +import docking.action.*; +import generic.theme.GIcon; import ghidra.app.ExamplesPluginPackage; import ghidra.app.events.ProgramLocationPluginEvent; import ghidra.app.plugin.PluginCategoryNames; @@ -28,15 +36,6 @@ import ghidra.program.model.listing.Program; import ghidra.util.HelpLocation; import ghidra.util.Msg; -import java.awt.Event; -import java.awt.event.KeyEvent; - -import javax.swing.*; - -import resources.ResourceManager; -import docking.ActionContext; -import docking.action.*; - /** * Class description goes here * @@ -54,120 +53,120 @@ import docking.action.*; ) //@formatter:on public class KitchenSinkPlugin extends ProgramPlugin { - private final static String NEXT_IMAGE = "images/right.png"; - private final static String PREV_IMAGE = "images/left.png"; - private DockingAction helloProgramAction; - private Program program; + private DockingAction helloProgramAction; + private Program program; - /** - * Constructor - */ - public KitchenSinkPlugin(PluginTool tool) { + /** + * Constructor + */ + public KitchenSinkPlugin(PluginTool tool) { - super(tool); + super(tool); - // set up list of services. - setupServices(); + // set up list of services. + setupServices(); - // set up list of actions. - setupActions(); - } - - private void setupServices() { - registerServiceProvided(HelloWorldService.class, - new HelloWorldService() { - public void sayHello() { - announce("Hello"); - } - }); - } - - private void setupActions() { - DockingAction action = new DockingAction("Hello World", getName() ) { - @Override - public void actionPerformed( ActionContext context ) { - Msg.info(this, "Hello World:: action"); - announce("Hello World"); - } - }; - action.setEnabled( true ); - String helloGroup = "Hello"; - ImageIcon prevImage = ResourceManager.loadImage(PREV_IMAGE); - action.setMenuBarData( new MenuData( new String[] {"Misc", "Hello World"}, prevImage, helloGroup ) ); - action.setPopupMenuData( new MenuData( new String[] {"Hello World"}, prevImage, helloGroup ) ); - action.setKeyBindingData( new KeyBindingData( KeyStroke.getKeyStroke('H', Event.CTRL_MASK ) ) ); - action.setToolBarData( new ToolBarData( prevImage, helloGroup ) ); - action.setDescription("Hello World"); - action.setHelpLocation(new HelpLocation("SampleHelpTopic", "KS_Hello_World")); - - tool.addAction(action); - - action = new DockingAction("Hello Program", getName() ) { - @Override - public void actionPerformed( ActionContext context ) { - Msg.info(this, "Hello Program:: action"); - sayHelloProgram(); - } - }; - action.setEnabled(false); - ImageIcon nextImage = ResourceManager.loadImage(NEXT_IMAGE); - action.setMenuBarData( new MenuData( new String[]{"Misc", "Hello Program"}, nextImage, helloGroup ) ); - action.setKeyBindingData( new KeyBindingData( KeyStroke.getKeyStroke(KeyEvent.VK_P, Event.CTRL_MASK ) ) ); - action.setToolBarData( new ToolBarData( nextImage, helloGroup ) ); - action.setDescription("Hello Program"); - action.setHelpLocation(new HelpLocation("SampleHelpTopic", "KS_Hello_Program")); - tool.addAction(action); - - // remember this action so I can enable/disable it later - helloProgramAction = action; - } - - @Override - protected void programActivated(Program activatedProgram) { - helloProgramAction.setEnabled(true); - this.program = activatedProgram; + // set up list of actions. + setupActions(); } + + private void setupServices() { + registerServiceProvided(HelloWorldService.class, + new HelloWorldService() { + public void sayHello() { + announce("Hello"); + } + }); + } + + private void setupActions() { + DockingAction action = new DockingAction("Hello World", getName()) { + @Override + public void actionPerformed(ActionContext context) { + Msg.info(this, "Hello World:: action"); + announce("Hello World"); + } + }; + action.setEnabled(true); + String helloGroup = "Hello"; + Icon prevImage = new GIcon("icon.sample.kitchen.sink.action.hello.world"); + action.setMenuBarData( + new MenuData(new String[] { "Misc", "Hello World" }, prevImage, helloGroup)); + action.setPopupMenuData( + new MenuData(new String[] { "Hello World" }, prevImage, helloGroup)); + action.setKeyBindingData(new KeyBindingData(KeyStroke.getKeyStroke('H', Event.CTRL_MASK))); + action.setToolBarData(new ToolBarData(prevImage, helloGroup)); + action.setDescription("Hello World"); + action.setHelpLocation(new HelpLocation("SampleHelpTopic", "KS_Hello_World")); + + tool.addAction(action); + + action = new DockingAction("Hello Program", getName()) { + @Override + public void actionPerformed(ActionContext context) { + Msg.info(this, "Hello Program:: action"); + sayHelloProgram(); + } + }; + action.setEnabled(false); + Icon nextImage = new GIcon("icon.sample.kitchen.sink.action.hello.program"); + action.setMenuBarData( + new MenuData(new String[] { "Misc", "Hello Program" }, nextImage, helloGroup)); + action.setKeyBindingData( + new KeyBindingData(KeyStroke.getKeyStroke(KeyEvent.VK_P, Event.CTRL_MASK))); + action.setToolBarData(new ToolBarData(nextImage, helloGroup)); + action.setDescription("Hello Program"); + action.setHelpLocation(new HelpLocation("SampleHelpTopic", "KS_Hello_Program")); + tool.addAction(action); + + // remember this action so I can enable/disable it later + helloProgramAction = action; + } + @Override - protected void programDeactivated(Program deactivatedProgram) { + protected void programActivated(Program activatedProgram) { + helloProgramAction.setEnabled(true); + this.program = activatedProgram; + } + + @Override + protected void programDeactivated(Program deactivatedProgram) { if (this.program == deactivatedProgram) { helloProgramAction.setEnabled(false); this.program = null; } } - protected void sayHelloProgram() { - if (program == null) { - return; - } + protected void sayHelloProgram() { - announce("Hello " + program.getName()); - } + if (program == null) { + return; + } - protected void announce(String message) { - JOptionPane.showMessageDialog(null,message,"Hello World", - JOptionPane.INFORMATION_MESSAGE); - } - - /** + announce("Hello " + program.getName()); + } + + protected void announce(String message) { + JOptionPane.showMessageDialog(null, message, "Hello World", + JOptionPane.INFORMATION_MESSAGE); + } + + /** * If your plugin maintains configuration state, you must save that state information - * to the SaveState object in this method. For example, the Code Browser can be configured - * to show fields in different colors. This is the method where that type - * information is saved. + * to the SaveState object in this method. For example, the Code Browser can be configured + * to show fields in different colors. This is the method where that type + * information is saved. */ - @Override - public void writeConfigState(SaveState saveState) { - } + @Override + public void writeConfigState(SaveState saveState) { + } + /** * If your plugin maintains configuration state, this is where you read it - * back in. + * back in. */ - @Override - public void readConfigState(SaveState saveState) { - } + @Override + public void readConfigState(SaveState saveState) { + } } - - - - - diff --git a/Ghidra/Extensions/sample/src/main/java/ghidra/examples/ShowInfoComponentProvider.java b/Ghidra/Extensions/sample/src/main/java/ghidra/examples/ShowInfoComponentProvider.java index c1b5de8566..6e61ae6b17 100644 --- a/Ghidra/Extensions/sample/src/main/java/ghidra/examples/ShowInfoComponentProvider.java +++ b/Ghidra/Extensions/sample/src/main/java/ghidra/examples/ShowInfoComponentProvider.java @@ -23,15 +23,15 @@ import docking.ActionContext; import docking.WindowPosition; import docking.action.DockingAction; import docking.action.ToolBarData; +import generic.theme.GIcon; import ghidra.framework.plugintool.ComponentProviderAdapter; import ghidra.framework.plugintool.PluginTool; import ghidra.program.model.listing.*; import ghidra.program.util.ProgramLocation; -import resources.ResourceManager; public class ShowInfoComponentProvider extends ComponentProviderAdapter { - private final static ImageIcon CLEAR_ICON = ResourceManager.loadImage("images/erase16.png"); - private final static ImageIcon INFO_ICON = ResourceManager.loadImage("images/information.png"); + private final static Icon CLEAR_ICON = new GIcon("icon.sample.hello.world.action.clear"); + private final static Icon PROVIDER_ICON = new GIcon("icon.sample.provider.hello.world"); private JPanel panel; private JTextArea textArea; @@ -42,7 +42,7 @@ public class ShowInfoComponentProvider extends ComponentProviderAdapter { public ShowInfoComponentProvider(PluginTool tool, String name) { super(tool, name, name); create(); - setIcon(INFO_ICON); + setIcon(PROVIDER_ICON); setDefaultWindowPosition(WindowPosition.BOTTOM); setTitle("Show Info"); setVisible(true); diff --git a/Ghidra/Extensions/sample/src/main/java/ghidra/examples/graph/SampleGraphPlugin.java b/Ghidra/Extensions/sample/src/main/java/ghidra/examples/graph/SampleGraphPlugin.java index 8b5bb9b706..8ce17a82d1 100644 --- a/Ghidra/Extensions/sample/src/main/java/ghidra/examples/graph/SampleGraphPlugin.java +++ b/Ghidra/Extensions/sample/src/main/java/ghidra/examples/graph/SampleGraphPlugin.java @@ -15,17 +15,17 @@ */ package ghidra.examples.graph; -import javax.swing.ImageIcon; +import javax.swing.Icon; import docking.ActionContext; import docking.action.DockingAction; import docking.action.ToolBarData; +import generic.theme.GIcon; import ghidra.app.ExamplesPluginPackage; import ghidra.app.plugin.PluginCategoryNames; import ghidra.framework.plugintool.*; import ghidra.framework.plugintool.util.PluginStatus; import ghidra.util.HelpLocation; -import resources.ResourceManager; /** * Sample plugin to demonstrate a plugin with a dockable GUI graph component @@ -64,8 +64,7 @@ public class SampleGraphPlugin extends Plugin { showProvider(); } }; - - ImageIcon icon = ResourceManager.loadImage("images/applications-development.png"); + Icon icon = new GIcon("icon.sample.action.show.graph"); showProviderAction.setToolBarData(new ToolBarData(icon, "View")); showProviderAction.setHelpLocation(DEFAULT_HELP); tool.addAction(showProviderAction); @@ -75,7 +74,6 @@ public class SampleGraphPlugin extends Plugin { provider.setVisible(true); } - @Override protected void dispose() { provider.dispose(); diff --git a/Ghidra/Extensions/sample/src/main/java/ghidra/examples/graph/layout/SampleGraphLayoutProvider.java b/Ghidra/Extensions/sample/src/main/java/ghidra/examples/graph/layout/SampleGraphLayoutProvider.java index 8bb3a27ec5..9e09fefab3 100644 --- a/Ghidra/Extensions/sample/src/main/java/ghidra/examples/graph/layout/SampleGraphLayoutProvider.java +++ b/Ghidra/Extensions/sample/src/main/java/ghidra/examples/graph/layout/SampleGraphLayoutProvider.java @@ -21,12 +21,12 @@ import java.util.Collection; import javax.swing.Icon; import edu.uci.ics.jung.algorithms.layout.Layout; +import generic.theme.GIcon; import ghidra.examples.graph.*; import ghidra.graph.viewer.layout.AbstractLayoutProvider; import ghidra.graph.viewer.layout.VisualGraphLayout; import ghidra.util.exception.CancelledException; import ghidra.util.task.TaskMonitor; -import resources.ResourceManager; /** * The layout provider for the {@link SampleGraphPlugin}. @@ -34,7 +34,7 @@ import resources.ResourceManager; public abstract class SampleGraphLayoutProvider extends AbstractLayoutProvider { - private static final Icon DEFAULT_ICON = ResourceManager.loadImage("images/color_swatch.png"); + private static final Icon DEFAULT_ICON = new GIcon("icon.sample.provider.graph"); @Override public abstract VisualGraphLayout getLayout(SampleGraph g, diff --git a/Ghidra/Extensions/sample/src/main/java/ghidra/examples/graph/layout/SampleGraphPluginDependencyLayoutProvider.java b/Ghidra/Extensions/sample/src/main/java/ghidra/examples/graph/layout/SampleGraphPluginDependencyLayoutProvider.java index 612f7a18cb..14b7bd72a7 100644 --- a/Ghidra/Extensions/sample/src/main/java/ghidra/examples/graph/layout/SampleGraphPluginDependencyLayoutProvider.java +++ b/Ghidra/Extensions/sample/src/main/java/ghidra/examples/graph/layout/SampleGraphPluginDependencyLayoutProvider.java @@ -17,12 +17,12 @@ package ghidra.examples.graph.layout; import javax.swing.Icon; +import generic.theme.GIcon; import ghidra.examples.graph.*; import ghidra.graph.viewer.layout.AbstractLayoutProvider; import ghidra.graph.viewer.layout.VisualGraphLayout; import ghidra.util.exception.CancelledException; import ghidra.util.task.TaskMonitor; -import resources.ResourceManager; /** * A layout provider for the {@link SampleGraphPlugin} @@ -31,7 +31,7 @@ public class SampleGraphPluginDependencyLayoutProvider extends AbstractLayoutProvider { private static final String NAME = "Plugin Dependency Layout"; - private static final Icon DEFAULT_ICON = ResourceManager.loadImage("images/color_swatch.png"); + private static final Icon DEFAULT_ICON = new GIcon("icon.sample.graph.dependency.layout"); @Override public VisualGraphLayout getLayout(SampleGraph g, TaskMonitor monitor) diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/searchmem/MemSearchPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/searchmem/MemSearchPlugin.java index c195a299bb..1ccd817b04 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/searchmem/MemSearchPlugin.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/searchmem/MemSearchPlugin.java @@ -94,8 +94,6 @@ public class MemSearchPlugin extends Plugin implements OptionsChangeListener, private int searchLimit; private static final Icon SEARCH_MARKER_ICON = new GIcon("icon.base.search.marker"); - private Color defaultHighlightColor; - private Color activeHighlightColor; private boolean doHighlight; private int byteGroupSize; private String byteDelimiter; @@ -389,10 +387,10 @@ public class MemSearchPlugin extends Plugin implements OptionsChangeListener, opt.registerOption(PluginConstants.SEARCH_HIGHLIGHT_NAME, true, null, "Toggles highlight search results"); - opt.registerOption(PluginConstants.SEARCH_HIGHLIGHT_COLOR_NAME, - PluginConstants.SEARCH_HIGHLIGHT_COLOR, null, "The search result highlight color"); - opt.registerOption(PluginConstants.SEARCH_HIGHLIGHT_CURRENT_COLOR_NAME, - PluginConstants.SEARCH_HIGHLIGHT_CURRENT_ADDR_COLOR, null, + opt.registerThemeColorOption(PluginConstants.SEARCH_HIGHLIGHT_COLOR_OPTION_NAME, + PluginConstants.SEARCH_HIGHLIGHT_COLOR_ID, null, "The search result highlight color"); + opt.registerThemeColorOption(PluginConstants.SEARCH_HIGHLIGHT_CURRENT_COLOR_OPTION_NAME, + PluginConstants.SEARCH_HIGHLIGHT_CURRENT_COLOR_ID, null, "The search result highlight color for the currently selected match"); opt.addOptionsChangeListener(this); @@ -413,10 +411,6 @@ public class MemSearchPlugin extends Plugin implements OptionsChangeListener, prepopulateSearch = opt.getBoolean(PluginConstants.PRE_POPULATE_MEM_SEARCH, true); autoRestrictSelection = opt.getBoolean(PluginConstants.AUTO_RESTRICT_SELECTION, true); doHighlight = opt.getBoolean(PluginConstants.SEARCH_HIGHLIGHT_NAME, true); - defaultHighlightColor = opt.getColor(PluginConstants.SEARCH_HIGHLIGHT_COLOR_NAME, - PluginConstants.SEARCH_HIGHLIGHT_COLOR); - activeHighlightColor = opt.getColor(PluginConstants.SEARCH_HIGHLIGHT_CURRENT_COLOR_NAME, - PluginConstants.SEARCH_HIGHLIGHT_CURRENT_ADDR_COLOR); opt = tool.getOptions(GhidraOptions.CATEGORY_BROWSER_FIELDS); byteGroupSize = opt.getInt(BytesFieldFactory.BYTE_GROUP_SIZE_MSG, 1); @@ -747,7 +741,7 @@ public class MemSearchPlugin extends Plugin implements OptionsChangeListener, private Color getHighlightColor(Address highlightStart, int highlightLength) { ProgramLocation location = navigatable != null ? navigatable.getLocation() : null; if (!(location instanceof BytesFieldLocation)) { - return defaultHighlightColor; + return PluginConstants.SEARCH_HIGHLIGHT_COLOR; } BytesFieldLocation byteLoc = (BytesFieldLocation) location; @@ -755,11 +749,12 @@ public class MemSearchPlugin extends Plugin implements OptionsChangeListener, if (highlightStart.hasSameAddressSpace(byteAddress)) { long diff = byteAddress.subtract(highlightStart); if (diff >= 0 && diff < highlightLength) { - return activeHighlightColor; // the current location is in the highlight + // the current location is in the highlight + return PluginConstants.SEARCH_HIGHLIGHT_CURRENT_ADDR_COLOR; } } - return defaultHighlightColor; + return PluginConstants.SEARCH_HIGHLIGHT_COLOR; } private boolean checkRemoveHighlights() { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/searchtext/SearchTextPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/searchtext/SearchTextPlugin.java index 44e851b813..e44b83f771 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/searchtext/SearchTextPlugin.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/searchtext/SearchTextPlugin.java @@ -15,7 +15,8 @@ */ package ghidra.app.plugin.core.searchtext; -import java.awt.*; +import java.awt.Component; +import java.awt.KeyboardFocusManager; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; @@ -99,8 +100,6 @@ public class SearchTextPlugin extends ProgramPlugin implements OptionsChangeList private int searchLimit; private SearchTask currentTask; private String lastSearchedText; - private Color highlightColor; - private Color currentAddrHighlightColor; private boolean doHighlight; private Navigatable navigatable; @@ -424,12 +423,6 @@ public class SearchTextPlugin extends ProgramPlugin implements OptionsChangeList } searchLimit = newSearchLimit; } - else if (optionName.equals(PluginConstants.SEARCH_HIGHLIGHT_CURRENT_COLOR_NAME)) { - currentAddrHighlightColor = (Color) newValue; - } - else if (optionName.equals(PluginConstants.SEARCH_HIGHLIGHT_COLOR_NAME)) { - highlightColor = (Color) newValue; - } else if (optionName.equals(PluginConstants.SEARCH_HIGHLIGHT_NAME)) { doHighlight = ((Boolean) newValue).booleanValue(); } @@ -442,22 +435,16 @@ public class SearchTextPlugin extends ProgramPlugin implements OptionsChangeList opt.registerOption(PluginConstants.SEARCH_HIGHLIGHT_NAME, true, loc, "Determines whether to highlight the matched string for a search in the listing."); - opt.registerOption(PluginConstants.SEARCH_HIGHLIGHT_COLOR_NAME, - PluginConstants.SEARCH_HIGHLIGHT_COLOR, loc, - "Color to use when highlighting the matched string for a search in the listing."); - opt.registerOption(PluginConstants.SEARCH_HIGHLIGHT_CURRENT_COLOR_NAME, - PluginConstants.SEARCH_HIGHLIGHT_COLOR, loc, - "Color to use for highlighting when the match string occurs at the current address."); + opt.registerThemeColorOption(PluginConstants.SEARCH_HIGHLIGHT_COLOR_OPTION_NAME, + PluginConstants.SEARCH_HIGHLIGHT_COLOR_ID, null, "The search result highlight color"); + opt.registerThemeColorOption(PluginConstants.SEARCH_HIGHLIGHT_CURRENT_COLOR_OPTION_NAME, + PluginConstants.SEARCH_HIGHLIGHT_CURRENT_COLOR_ID, null, + "The search result highlight color for the currently selected match"); searchLimit = opt.getInt(GhidraOptions.OPTION_SEARCH_LIMIT, PluginConstants.DEFAULT_SEARCH_LIMIT); doHighlight = opt.getBoolean(PluginConstants.SEARCH_HIGHLIGHT_NAME, true); - highlightColor = opt.getColor(PluginConstants.SEARCH_HIGHLIGHT_COLOR_NAME, - PluginConstants.SEARCH_HIGHLIGHT_COLOR); - currentAddrHighlightColor = - opt.getColor(PluginConstants.SEARCH_HIGHLIGHT_CURRENT_COLOR_NAME, - PluginConstants.SEARCH_HIGHLIGHT_CURRENT_ADDR_COLOR); opt.setOptionsHelpLocation(new HelpLocation(HelpTopics.SEARCH, "Search_Text")); @@ -661,12 +648,14 @@ public class SearchTextPlugin extends ProgramPlugin implements OptionsChangeList int start = matcher.start(); int end = matcher.end() - 1; if (start <= cursorTextOffset && end >= cursorTextOffset) { - list.add(new Highlight(start, end, currentAddrHighlightColor)); + list.add(new Highlight(start, end, + PluginConstants.SEARCH_HIGHLIGHT_CURRENT_ADDR_COLOR)); } else if (loc == null) { // only add in matches around current match if loc is null // meaning that this is a one at a time search and not a table // of results. - list.add(new Highlight(start, end, highlightColor)); + list.add(new Highlight(start, end, + PluginConstants.SEARCH_HIGHLIGHT_CURRENT_ADDR_COLOR)); } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/PluginConstants.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/PluginConstants.java index 310d8d2a15..a11d2be67e 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/PluginConstants.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/PluginConstants.java @@ -77,17 +77,10 @@ public interface PluginConstants { * Name of the Options object for Search. */ public static final String SEARCH_OPTION_NAME = "Search"; - /** - * Option name for highlight color - */ - public static final String SEARCH_HIGHLIGHT_COLOR_NAME = " Highlight Color"; - /** * Option name for highlight color used when something to highlight is at the current * address. */ - public static final String SEARCH_HIGHLIGHT_CURRENT_COLOR_NAME = - "Highlight Color for Current Match"; /** * Option name for whether to highlight search results. */ @@ -96,13 +89,19 @@ public interface PluginConstants { /** * Color for highlighting for searches. */ - public static final Color SEARCH_HIGHLIGHT_COLOR = new GColor("color.bg.search.highlight"); + public static final String SEARCH_HIGHLIGHT_COLOR_OPTION_NAME = " Highlight Color"; + public static final String SEARCH_HIGHLIGHT_COLOR_ID = "color.bg.search.highlight"; + public static final Color SEARCH_HIGHLIGHT_COLOR = new GColor(SEARCH_HIGHLIGHT_COLOR_ID); /** * Default highlight color used when something to highlight is at the current * address. */ + public static final String SEARCH_HIGHLIGHT_CURRENT_COLOR_OPTION_NAME = + "Highlight Color for Current Match"; + public static final String SEARCH_HIGHLIGHT_CURRENT_COLOR_ID = + "color.bg.search.current.line.highlight"; public static final Color SEARCH_HIGHLIGHT_CURRENT_ADDR_COLOR = - new GColor("color.bg.search.current.line.highlight"); + new GColor(SEARCH_HIGHLIGHT_CURRENT_COLOR_ID); } diff --git a/Ghidra/Framework/Docking/data/docking.theme.properties b/Ghidra/Framework/Docking/data/docking.theme.properties index 95dbd1ce53..e30b607fe0 100644 --- a/Ghidra/Framework/Docking/data/docking.theme.properties +++ b/Ghidra/Framework/Docking/data/docking.theme.properties @@ -88,6 +88,8 @@ color.fg.fieldpanel = color.fg color.bg.fieldpanel.selection = color.bg.selection color.bg.fieldpanel.highlight = color.bg.highlight color.bg.fieldpanel.selection.and.highlight = green +// docking buttons +color.fg.button = black // Icons files icon.empty = EmptyIcon16.gif @@ -207,4 +209,6 @@ color.bg.tableheader.gradient.end = darkGray color.bg.tableheader.gradient.start.primary = color.bg color.bg.tableheader.gradient.end.primary = darkBlue +// docking buttons +color.fg.button = darkGray diff --git a/Ghidra/Framework/Docking/src/main/java/docking/CloseIcon.java b/Ghidra/Framework/Docking/src/main/java/docking/CloseIcon.java new file mode 100644 index 0000000000..77f9db4ad7 --- /dev/null +++ b/Ghidra/Framework/Docking/src/main/java/docking/CloseIcon.java @@ -0,0 +1,67 @@ +/* ### + * 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 docking; + +import java.awt.*; + +import javax.swing.Icon; + +/** + * Icon for a close button + */ +public class CloseIcon implements Icon { + private int size; + private int margin; + private Color color; + + /** + * Creates a close icon. + * @param size the width and height of the icon + * @param margin the margin around the "x" + * @param color the color of the "x" + */ + public CloseIcon(int size, int margin, Color color) { + this.size = size; + this.margin = margin; + this.color = color; + } + + @Override + public void paintIcon(Component c, Graphics g, int x, int y) { + g.setColor(color); + int xStart = x + margin; + int yStart = y + margin; + int xEnd = x + size - margin; + int yEnd = y + size - margin; + g.drawLine(xStart, yStart, xEnd, yEnd); + g.drawLine(xStart, yEnd, xEnd, yStart); + g.drawLine(xStart + 1, yStart, xEnd + 1, yEnd); + g.drawLine(xStart + 1, yEnd, xEnd + 1, yStart); + g.drawLine(xStart - 1, yStart, xEnd - 1, yEnd); + g.drawLine(xStart - 1, yEnd, xEnd - 1, yStart); + } + + @Override + public int getIconWidth() { + return size; + } + + @Override + public int getIconHeight() { + return size; + } + +} diff --git a/Ghidra/Framework/Docking/src/main/java/docking/DockableToolBarManager.java b/Ghidra/Framework/Docking/src/main/java/docking/DockableToolBarManager.java index 82e8c35169..89ef71a1e1 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/DockableToolBarManager.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/DockableToolBarManager.java @@ -15,8 +15,7 @@ */ package docking; -import java.awt.Dimension; -import java.awt.FlowLayout; +import java.awt.*; import java.util.Collections; import java.util.Iterator; @@ -24,17 +23,17 @@ import javax.swing.*; import docking.action.*; import docking.menu.*; +import generic.theme.GColor; import ghidra.util.exception.AssertException; import ghidra.util.task.SwingUpdateManager; -import resources.ResourceManager; /** * Manages to toolbar for the dockable components. */ class DockableToolBarManager { - private static final ImageIcon CLOSE_ICON = ResourceManager.loadImage("images/close16.gif"); - private static final ImageIcon MENU_ICON = ResourceManager.loadImage("images/menu16.gif"); - + private static final Color BUTTON_COLOR = new GColor("color.fg.button"); + private static final Icon CLOSE_ICON = new CloseIcon(16, 4, BUTTON_COLOR); + private static final Icon MENU_ICON = new DropDownMenuIcon(16, 4, 4, BUTTON_COLOR); private GenericHeader dockableHeader; private ToolBarManager toolBarManager; diff --git a/Ghidra/Framework/Docking/src/main/java/docking/DropDownMenuIcon.java b/Ghidra/Framework/Docking/src/main/java/docking/DropDownMenuIcon.java new file mode 100644 index 0000000000..bf9692d96a --- /dev/null +++ b/Ghidra/Framework/Docking/src/main/java/docking/DropDownMenuIcon.java @@ -0,0 +1,71 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package docking; + +import java.awt.*; + +import javax.swing.Icon; + +/** + * Icon for a drop down menu button (triangle pointing down) + */ +public class DropDownMenuIcon implements Icon { + private int size; + private int xMargin; + private int yMargin; + private Color color; + + /** + * Creates a drop down menu icon. + * @param size the width and height of the icon + * @param xMargin the margin around triangle base + * @param yMargin the margin around triangle height + * @param color the color of the triangle + */ + public DropDownMenuIcon(int size, int xMargin, int yMargin, Color color) { + this.size = size; + this.xMargin = xMargin; + this.yMargin = yMargin; + this.color = color; + } + + @Override + public void paintIcon(Component c, Graphics g, int x, int y) { + g.setColor(color); + + // draw a triangle pointing down + int p1x = x + size / 2; + int p1y = y + size - yMargin; + int p2x = x + xMargin; + int p2y = y + yMargin; + int p3x = x + size - xMargin + 1; + int p3y = y + yMargin; + int xPoints[] = { p1x, p2x, p3x }; + int yPoints[] = { p1y, p2y, p3y }; + g.fillPolygon(xPoints, yPoints, 3); + } + + @Override + public int getIconWidth() { + return size; + } + + @Override + public int getIconHeight() { + return size; + } + +} diff --git a/Ghidra/Framework/Docking/src/main/java/docking/util/image/ToolIconURL.java b/Ghidra/Framework/Docking/src/main/java/docking/util/image/ToolIconURL.java index a949db7312..3231181e55 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/util/image/ToolIconURL.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/util/image/ToolIconURL.java @@ -21,7 +21,6 @@ import java.util.Hashtable; import javax.swing.ImageIcon; -import generic.Images; import resources.ResourceManager; import resources.icons.UnresolvedIcon; @@ -66,7 +65,7 @@ public class ToolIconURL implements Comparable { */ public ToolIconURL(String location) { if (location == null) { - location = Images.BOMB; + location = ResourceManager.BOMB; } this.location = location; @@ -168,7 +167,7 @@ public class ToolIconURL implements Comparable { // O.K., we will scale the icon. However, if it is the default icon, we know we have // a 'large' version of that. if (unscaledIcon instanceof UnresolvedIcon) { - return ResourceManager.loadImage(Images.BIG_BOMB); + return ResourceManager.loadImage(ResourceManager.BIG_BOMB); } return ResourceManager.getScaledIcon(unscaledIcon, LARGE_ICON_SIZE, LARGE_ICON_SIZE); diff --git a/Ghidra/Framework/Generic/src/main/java/generic/Images.java b/Ghidra/Framework/Generic/src/main/java/generic/Images.java deleted file mode 100644 index 13e762713b..0000000000 --- a/Ghidra/Framework/Generic/src/main/java/generic/Images.java +++ /dev/null @@ -1,22 +0,0 @@ -/* ### - * IP: GHIDRA - * REVIEWED: YES - * - * 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 generic; - -public class Images { - public static String BOMB = "images/core.png"; - public static String BIG_BOMB = "images/core24.png"; -} diff --git a/Ghidra/Framework/Generic/src/main/java/generic/theme/AbstractThemeReader.java b/Ghidra/Framework/Generic/src/main/java/generic/theme/AbstractThemeReader.java index a3846f0492..80b5b95b4d 100644 --- a/Ghidra/Framework/Generic/src/main/java/generic/theme/AbstractThemeReader.java +++ b/Ghidra/Framework/Generic/src/main/java/generic/theme/AbstractThemeReader.java @@ -99,7 +99,8 @@ public abstract class AbstractThemeReader { return IconValue.parse(key, value); } catch (ParseException e) { - error(lineNumber, "Could not parse Icon value: " + value + "because " + e.getMessage()); + error(lineNumber, + "Could not parse Icon value: \"" + value + "\" because: " + e.getMessage()); } return null; } diff --git a/Ghidra/Framework/Generic/src/main/java/generic/theme/Gui.java b/Ghidra/Framework/Generic/src/main/java/generic/theme/Gui.java index 8ee0d48dee..27570ac605 100644 --- a/Ghidra/Framework/Generic/src/main/java/generic/theme/Gui.java +++ b/Ghidra/Framework/Generic/src/main/java/generic/theme/Gui.java @@ -61,6 +61,7 @@ public class Gui { private static GThemeValueMap applicationDarkDefaults = new GThemeValueMap(); private static GThemeValueMap javaDefaults = new GThemeValueMap(); private static GThemeValueMap currentValues = new GThemeValueMap(); + private static GThemeValueMap systemValues = new GThemeValueMap(); private static ThemeFileLoader themeFileLoader = new ThemeFileLoader(); private static ThemePreferenceManager themePreferenceManager = new ThemePreferenceManager(); @@ -114,6 +115,72 @@ public class Gui { notifyThemeChanged(new AllValuesChangedThemeEvent(false)); } + /** + * Restores the current color value for the given color id to the value established by the + * current theme. + * @param id the color id to restore back to the original theme value + */ + public static void restoreColor(String id) { + if (changedValuesMap.containsColor(id)) { + Gui.setColor(changedValuesMap.getColor(id)); + } + } + + /** + * Restores the current font value for the given font id to the value established by the + * current theme. + * @param id the font id to restore back to the original theme value + */ + public static void restoreFont(String id) { + if (changedValuesMap.containsFont(id)) { + Gui.setFont(changedValuesMap.getFont(id)); + } + } + + /** + * Restores the current icon value for the given icon id to the value established by the + * current theme. + * @param id the icon id to restore back to the original theme value + */ + public static void restoreIcon(String id) { + if (changedValuesMap.containsIcon(id)) { + Gui.setIcon(changedValuesMap.getIcon(id)); + } + } + + /** + * Returns true if the color associated with the given id has been changed from the current + * theme value for that id. + * @param id the color id to check if it has been changed + * @return true if the color associated with the given id has been changed from the current + * theme value for that id. + */ + public static boolean isChangedColor(String id) { + return changedValuesMap.containsColor(id); + } + + /** + * Returns true if the font associated with the given id has been changed from the current + * theme value for that id. + * @param id the font id to check if it has been changed + * @return true if the font associated with the given id has been changed from the current + * theme value for that id. + */ + public static boolean isChangedFont(String id) { + return changedValuesMap.containsFont(id); + } + + /** + * Returns true if the Icon associated with the given id has been changed from the current + * theme value for that id. + * @param id the Icon id to check if it has been changed + * @return true if the Icon associated with the given id has been changed from the current + * theme value for that id. + */ + public static boolean isChangedIcon(String id) { + return changedValuesMap.containsIcon(id); + } + /** * Sets the application's active theme to the given theme. * @param theme the theme to make active @@ -125,8 +192,8 @@ public class Gui { lookAndFeelManager = lookAndFeel.getLookAndFeelManager(); try { lookAndFeelManager.installLookAndFeel(); - notifyThemeChanged(new AllValuesChangedThemeEvent(true)); themePreferenceManager.saveThemeToPreferences(theme); + notifyThemeChanged(new AllValuesChangedThemeEvent(true)); } catch (Exception e) { Msg.error(Gui.class, @@ -388,6 +455,10 @@ public class Gui { return gIcon; } + public static void setSystemDefaults(GThemeValueMap map) { + systemValues = map; + } + /** * Sets the map of JavaDefaults defined by the current {@link LookAndFeel}. * @param map the default theme values defined by the {@link LookAndFeel} @@ -442,6 +513,7 @@ public class Gui { */ public static GThemeValueMap getDefaults() { GThemeValueMap currentDefaults = new GThemeValueMap(javaDefaults); + currentDefaults.load(systemValues); currentDefaults.load(applicationDefaults); if (activeTheme.useDarkDefaults()) { currentDefaults.load(applicationDarkDefaults); @@ -626,6 +698,7 @@ public class Gui { GThemeValueMap map = new GThemeValueMap(); map.load(javaDefaults); + map.load(systemValues); map.load(applicationDefaults); if (activeTheme.useDarkDefaults()) { map.load(applicationDarkDefaults); diff --git a/Ghidra/Framework/Generic/src/main/java/generic/theme/IconValue.java b/Ghidra/Framework/Generic/src/main/java/generic/theme/IconValue.java index 4f2451cbaf..09a426078f 100644 --- a/Ghidra/Framework/Generic/src/main/java/generic/theme/IconValue.java +++ b/Ghidra/Framework/Generic/src/main/java/generic/theme/IconValue.java @@ -149,11 +149,15 @@ public class IconValue extends ThemeValue { return new IconValue(id, icon, modifier); } - private static Icon getIcon(String baseIconString) { + private static Icon getIcon(String baseIconString) throws ParseException { if (EMPTY_ICON_STRING.equals(baseIconString)) { return new EmptyIcon(STANDARD_EMPTY_ICON_SIZE, STANDARD_EMPTY_ICON_SIZE); } - return ResourceManager.loadImage(baseIconString); + Icon icon = ResourceManager.loadIcon(baseIconString); + if (icon == null) { + throw new ParseException("Can't find icon for \"" + baseIconString + "\"", 0); + } + return icon; } private static IconValue parseRefIcon(String id, String value) throws ParseException { diff --git a/Ghidra/Framework/Generic/src/main/java/generic/theme/laf/LookAndFeelManager.java b/Ghidra/Framework/Generic/src/main/java/generic/theme/laf/LookAndFeelManager.java index 6b2afa1b26..27088002c1 100644 --- a/Ghidra/Framework/Generic/src/main/java/generic/theme/laf/LookAndFeelManager.java +++ b/Ghidra/Framework/Generic/src/main/java/generic/theme/laf/LookAndFeelManager.java @@ -84,6 +84,7 @@ public abstract class LookAndFeelManager { IllegalAccessException, UnsupportedLookAndFeelException { cleanUiDefaults(); + Gui.setSystemDefaults(systemToLafMap); doInstallLookAndFeel(); installJavaDefaults(); fixupLookAndFeelIssues(); @@ -262,7 +263,6 @@ public abstract class LookAndFeelManager { */ private void installJavaDefaults() { GThemeValueMap javaDefaults = extractJavaDefaults(); - javaDefaults.load(systemToLafMap); // add in our system color mappings ThemeGrouper grouper = getThemeGrouper(); grouper.group(javaDefaults); Gui.setJavaDefaults(javaDefaults); @@ -331,7 +331,6 @@ public abstract class LookAndFeelManager { Icon icon = UIManager.getIcon(id); values.addIcon(new IconValue(id, icon)); } - return values; } diff --git a/Ghidra/Framework/Generic/src/main/java/ghidra/framework/options/AbstractOptions.java b/Ghidra/Framework/Generic/src/main/java/ghidra/framework/options/AbstractOptions.java index b199b3f091..dd25542adf 100644 --- a/Ghidra/Framework/Generic/src/main/java/ghidra/framework/options/AbstractOptions.java +++ b/Ghidra/Framework/Generic/src/main/java/ghidra/framework/options/AbstractOptions.java @@ -131,6 +131,10 @@ public abstract class AbstractOptions implements Options { public synchronized void registerOption(String optionName, OptionType type, Object defaultValue, HelpLocation help, String description, PropertyEditor editor) { + if (type == OptionType.COLOR_TYPE) { +// Msg.warn(this, "Registering color: " + optionName, +// ReflectionUtilities.createJavaFilteredThrowable()); + } if (type == OptionType.NO_TYPE) { throw new IllegalArgumentException( "Can't register an option of type: " + OptionType.NO_TYPE); @@ -151,7 +155,7 @@ public abstract class AbstractOptions implements Options { ReflectionUtilities.createJavaFilteredThrowable()); } - Option currentOption = getExistingComptibleOption(optionName, type, defaultValue); + Option currentOption = getExistingComptibleOption(optionName, type); if (currentOption != null) { currentOption.updateRegistration(description, help, defaultValue, editor); return; @@ -163,8 +167,31 @@ public abstract class AbstractOptions implements Options { valueMap.put(optionName, option); } - private Option getExistingComptibleOption(String optionName, OptionType type, - Object defaultValue) { + @Override + public void registerThemeColorOption(String optionName, String colorId, HelpLocation help, + String description) { + Option currentOption = getExistingComptibleOption(optionName, OptionType.COLOR_TYPE); + if (currentOption != null && currentOption instanceof ThemeColorOption) { + currentOption.updateRegistration(description, help, null, null); + return; + } + Option option = new ThemeColorOption(optionName, colorId, description, help); + valueMap.put(optionName, option); + } + + @Override + public void registerThemeFontOption(String optionName, String fontId, HelpLocation help, + String description) { + Option currentOption = getExistingComptibleOption(optionName, OptionType.FONT_TYPE); + if (currentOption != null && currentOption instanceof ThemeFontOption) { + currentOption.updateRegistration(description, help, null, null); + return; + } + Option option = new ThemeFontOption(optionName, fontId, description, help); + valueMap.put(optionName, option); + } + + private Option getExistingComptibleOption(String optionName, OptionType type) { // There are several cases where an existing option may exist when registering an option // 1) the option was accessed before it was registered diff --git a/Ghidra/Framework/Generic/src/main/java/ghidra/framework/options/Options.java b/Ghidra/Framework/Generic/src/main/java/ghidra/framework/options/Options.java index 256d38df63..8c21f59f29 100644 --- a/Ghidra/Framework/Generic/src/main/java/ghidra/framework/options/Options.java +++ b/Ghidra/Framework/Generic/src/main/java/ghidra/framework/options/Options.java @@ -149,6 +149,32 @@ public interface Options { public void registerOption(String optionName, OptionType type, Object defaultValue, HelpLocation help, String description, PropertyEditor editor); + /** + * Registers a theme color as an option. Doing this allows the color to be edited in the + * options Gui, but all changes result in a theme change and not an options change. In other + * words, changes to this option causes a theme change and is saved to a theme instead of in + * the tool with normal options + * @param optionName the option name to bind to a theme color id + * @param colorId the theme color id to be affected if a users changes this color option + * @param help the {@link HelpLocation} for more information on how this color is used + * @param description a short description of how this color is used + */ + public void registerThemeColorOption(String optionName, String colorId, HelpLocation help, + String description); + + /** + * Registers a theme font as an option. Doing this allows the font to be edited in the + * options Gui, but all changes result in a theme change and not an options change. In other + * words, changes to this option causes a theme change and is saved to a theme instead of in + * the tool with normal options + * @param optionName the option name to bind to a theme font id + * @param fontId the theme font id to be affected if a users changes this font option + * @param help the {@link HelpLocation} for more information on how this font is used + * @param description a short description of how this font is used + */ + public void registerThemeFontOption(String optionName, String fontId, HelpLocation help, + String description); + /** * Register the options editor that will handle the editing for all the options or a sub group of options. * @param editor the custom editor panel to be used to edit the options or sub group of options. diff --git a/Ghidra/Framework/Generic/src/main/java/ghidra/framework/options/SubOptions.java b/Ghidra/Framework/Generic/src/main/java/ghidra/framework/options/SubOptions.java index 734bbccf2f..9d3cdbbffe 100644 --- a/Ghidra/Framework/Generic/src/main/java/ghidra/framework/options/SubOptions.java +++ b/Ghidra/Framework/Generic/src/main/java/ghidra/framework/options/SubOptions.java @@ -92,6 +92,18 @@ public class SubOptions implements Options { options.registerOption(prefix + optionName, type, defaultValue, help, description, editor); } + @Override + public void registerThemeColorOption(String optionName, String colorId, HelpLocation help, + String description) { + options.registerThemeColorOption(prefix + optionName, colorId, help, description); + } + + @Override + public void registerThemeFontOption(String optionName, String fontId, HelpLocation help, + String description) { + options.registerThemeFontOption(prefix + optionName, fontId, help, description); + } + @Override public void putObject(String optionName, Object obj) { options.putObject(prefix + optionName, obj); diff --git a/Ghidra/Framework/Generic/src/main/java/ghidra/framework/options/ThemeColorOption.java b/Ghidra/Framework/Generic/src/main/java/ghidra/framework/options/ThemeColorOption.java new file mode 100644 index 0000000000..40af93f4f5 --- /dev/null +++ b/Ghidra/Framework/Generic/src/main/java/ghidra/framework/options/ThemeColorOption.java @@ -0,0 +1,58 @@ +/* ### + * 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.framework.options; + +import java.awt.Color; + +import generic.theme.Gui; +import ghidra.util.HelpLocation; + +/** + * Options implementation for theme color options. A ThemeColorOption is an option that, when + * changed, affects the current theme and is saved in the theme, instead of being saved with + * normal non-theme related options. + */ +public class ThemeColorOption extends Option { + + private String colorId; + + public ThemeColorOption(String optionName, String colorId, String description, + HelpLocation help) { + super(optionName, OptionType.COLOR_TYPE, description, help, null, true, null); + this.colorId = colorId; + } + + @Override + public Color getCurrentValue() { + return Gui.getColor(colorId); + } + + @Override + public void doSetCurrentValue(Object value) { + Gui.setColor(colorId, (Color) value); + } + + @Override + public boolean isDefault() { + return !Gui.isChangedColor(colorId); + } + + @Override + public void restoreDefault() { + Gui.restoreColor(colorId); + } + +} diff --git a/Ghidra/Framework/Generic/src/main/java/ghidra/framework/options/ThemeFontOption.java b/Ghidra/Framework/Generic/src/main/java/ghidra/framework/options/ThemeFontOption.java new file mode 100644 index 0000000000..5174dd862e --- /dev/null +++ b/Ghidra/Framework/Generic/src/main/java/ghidra/framework/options/ThemeFontOption.java @@ -0,0 +1,58 @@ +/* ### + * 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.framework.options; + +import java.awt.Font; + +import generic.theme.Gui; +import ghidra.util.HelpLocation; + +/** + * Options implementation for theme font options. A ThemeFontOption is an option that, when + * changed, affects the current theme and is saved in the theme, instead of being saved with + * normal non-theme related options. + */ +public class ThemeFontOption extends Option { + + private String fontId; + + public ThemeFontOption(String optionName, String fontId, String description, + HelpLocation help) { + super(optionName, OptionType.FONT_TYPE, description, help, null, true, null); + this.fontId = fontId; + } + + @Override + public Font getCurrentValue() { + return Gui.getFont(fontId); + } + + @Override + public void doSetCurrentValue(Object value) { + Gui.setFont(fontId, (Font) value); + } + + @Override + public boolean isDefault() { + return !Gui.isChangedFont(fontId); + } + + @Override + public void restoreDefault() { + Gui.restoreFont(fontId); + } + +} diff --git a/Ghidra/Framework/Generic/src/main/java/resources/IconProvider.java b/Ghidra/Framework/Generic/src/main/java/resources/IconProvider.java index 1ea123f154..823271da59 100644 --- a/Ghidra/Framework/Generic/src/main/java/resources/IconProvider.java +++ b/Ghidra/Framework/Generic/src/main/java/resources/IconProvider.java @@ -24,7 +24,6 @@ import java.net.URL; import javax.swing.Icon; import javax.swing.ImageIcon; -import generic.Images; import generic.util.image.ImageUtils; import ghidra.util.Msg; @@ -123,6 +122,6 @@ public class IconProvider { } private URL getDefaultUrl() { - return ResourceManager.getResource(Images.BOMB); + return ResourceManager.getResource(ResourceManager.BOMB); } } diff --git a/Ghidra/Framework/Generic/src/main/java/resources/ResourceManager.java b/Ghidra/Framework/Generic/src/main/java/resources/ResourceManager.java index 39ba9ff0b6..ccf832795c 100644 --- a/Ghidra/Framework/Generic/src/main/java/resources/ResourceManager.java +++ b/Ghidra/Framework/Generic/src/main/java/resources/ResourceManager.java @@ -15,13 +15,13 @@ */ package resources; -import java.awt.Image; -import java.awt.MediaTracker; +import java.awt.*; import java.awt.image.BufferedImage; import java.io.*; import java.net.*; import java.nio.file.Path; import java.util.*; +import java.util.List; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.stream.Collectors; @@ -30,7 +30,6 @@ import javax.swing.*; import org.apache.commons.lang3.StringUtils; -import generic.Images; import generic.theme.GIcon; import ghidra.framework.Application; import ghidra.util.Msg; @@ -48,15 +47,17 @@ import utility.module.ModuleUtilities; * as opposed to using the flawed constructor {@link ImageIcon#ImageIcon(Image)}. */ public class ResourceManager { - public final static String EXTERNAL_ICON_PREFIX = "[EXTERNAL]"; - private final static String DEFAULT_ICON_FILENAME = Images.BOMB; - private static ImageIcon DEFAULT_ICON; - private static Map iconMap = new HashMap<>(); + public static final String BOMB = "images/core.png"; + public static final String BIG_BOMB = "images/core24.png"; + public static final String EXTERNAL_ICON_PREFIX = "[EXTERNAL]"; + + private static final Map iconMap = new HashMap<>(); private static List defaultSearchPaths; private static List testSearchPaths; private static ClassLoader classLoader = ResourceManager.class.getClassLoader(); + private static final ImageIcon DEFAULT_ICON = loadDefaultIcon(); /** * Finds a resource with a given name. This method returns null if no @@ -441,26 +442,6 @@ public class ResourceManager { if (icon instanceof GIcon) { return ((GIcon) icon).getId(); } - - /* - TODO - not sure why we wanted just the name and not the entire URL? Delete this - after a bit - - if (iconName == null) { - return null; - } - - int pos = iconName.lastIndexOf(File.separator); - if (pos >= 0) { - iconName = iconName.substring(pos + 1); - } - else { - pos = iconName.lastIndexOf("/"); - if (pos >= 0) { - iconName = iconName.substring(pos + 1); - } - } - */ return iconName; } @@ -496,7 +477,7 @@ public class ResourceManager { if (loadImage == null) { return null; } - return (ImageIcon) getScaledIcon(loadImage, width, height); + return getScaledIcon(loadImage, width, height); } /** @@ -520,24 +501,43 @@ public class ResourceManager { return icon; } + /** + * Load the icon specified by iconPath. The iconPath can be either a path to a resource on + * the classpath or a relative or absolute path to an icon on the file system. If the iconPath + * is a path to a classpath resource, then it will be searched directly or also with an "images/" + * prepended to the path. For example, if there exists an icon "home.gif" on the classpath that + * was stored in the standard "images" resource directory, then it exists on the classpath + * as "images/home.gif". That icon will be found if the iconPath is either "images/home.gif" or + * just as "home.gif". + * + * @param iconPath name of file to load, e.g., "images/home.gif" + * @return an Icon from the given iconPath or null, if no such icon can be found + */ + + public static Icon loadIcon(String iconPath) { + ImageIcon icon = iconMap.get(iconPath); + if (icon == null) { + icon = doLoadIcon(iconPath); + iconMap.put(iconPath, icon == null ? DEFAULT_ICON : icon); + } + + return icon == DEFAULT_ICON ? null : icon; + } + /** * Load the image specified by filename; returns the default bomb icon * if problems occur trying to load the file. * - * @param filename name of file to load, e.g., "images/home.gif" + * @param iconPath name of file to load, e.g., "images/home.gif" * @return the image icon stored in the bytes */ - public static ImageIcon loadImage(String filename) { - ImageIcon icon = iconMap.get(filename); + public static ImageIcon loadImage(String iconPath) { + ImageIcon icon = iconMap.get(iconPath); if (icon == null) { - icon = doLoadIcon(filename); - if (icon == null) { - Msg.warn(ResourceManager.class, "Can't resolve icon: " + filename); - icon = new UnresolvedIcon(filename, getDefaultIcon()); - } - iconMap.put(filename, icon); + icon = doLoadIcon(iconPath); + iconMap.put(iconPath, icon == null ? DEFAULT_ICON : icon); } - return icon; + return icon == null ? new UnresolvedIcon(iconPath, DEFAULT_ICON) : icon; } public static Set getLoadedUrlIcons() { @@ -551,7 +551,7 @@ public class ResourceManager { return icons; } - private static ImageIcon doLoadIcon(String path) { + private static UrlImageIcon doLoadIcon(String path) { // if the has the "external prefix", it is an icon in the user's application directory if (path.startsWith(EXTERNAL_ICON_PREFIX)) { @@ -628,14 +628,6 @@ public class ResourceManager { } public static ImageIcon getDefaultIcon() { - if (DEFAULT_ICON == null) { - URL url = getResource(DEFAULT_ICON_FILENAME); - if (url == null) { - Msg.error(ResourceManager.class, - "Could not find default icon: " + DEFAULT_ICON_FILENAME); - } - DEFAULT_ICON = new UrlImageIcon(DEFAULT_ICON_FILENAME, url); - } return DEFAULT_ICON; } @@ -645,6 +637,16 @@ public class ResourceManager { return list; } + private static ImageIcon loadDefaultIcon() { + URL url = getResource(BOMB); + if (url != null) { + return new UrlImageIcon(BOMB, url); + } + Msg.error(ResourceManager.class, + "Could not find default icon: " + BOMB); + return getImageIcon(new ColorIcon3D(Color.RED, 16, 16)); + } + private static void filterImages(Set set) { Iterator it = set.iterator(); while (it.hasNext()) { diff --git a/Ghidra/Framework/Help/src/main/java/help/CustomTOCView.java b/Ghidra/Framework/Help/src/main/java/help/CustomTOCView.java index 942bffb27d..4764282c01 100644 --- a/Ghidra/Framework/Help/src/main/java/help/CustomTOCView.java +++ b/Ghidra/Framework/Help/src/main/java/help/CustomTOCView.java @@ -74,6 +74,9 @@ public class CustomTOCView extends TOCView { } public HelpModel getHelpModel() { + if (ui == null) { + return null; + } return ui.getHelpModel(); }