GP-1981 added in font support and Icon support

This commit is contained in:
ghidragon 2022-07-28 15:46:44 -04:00
parent cd4ab3a156
commit 703a7beb8d
56 changed files with 2129 additions and 1060 deletions

View File

@ -61,7 +61,7 @@ public class CallTreeProvider extends ComponentProviderAdapter implements Domain
private static final Icon EXPAND_ICON = Icons.EXPAND_ALL_ICON;
private static final Icon COLLAPSE_ICON = Icons.COLLAPSE_ALL_ICON;
private static ImageIcon REFRESH_ICON = Icons.REFRESH_ICON;
private static Icon REFRESH_ICON = Icons.REFRESH_ICON;
private static Icon REFRESH_NOT_NEEDED_ICON = ResourceManager.getDisabledIcon(REFRESH_ICON, 60);
private static final String RECURSE_DEPTH_PROPERTY_NAME = "call.tree.recurse.depth";
@ -350,8 +350,8 @@ public class CallTreeProvider extends ComponentProviderAdapter implements Domain
return true;
}
};
goToSourceAction.setPopupMenuData(
new MenuData(new String[] { "Go To Call Source" }, goToMenu));
goToSourceAction
.setPopupMenuData(new MenuData(new String[] { "Go To Call Source" }, goToMenu));
goToSourceAction.setHelpLocation(
new HelpLocation(plugin.getName(), "Call_Tree_Context_Action_Goto_Source"));
tool.addLocalAction(this, goToSourceAction);
@ -369,8 +369,8 @@ public class CallTreeProvider extends ComponentProviderAdapter implements Domain
new ToolBarData(ResourceManager.loadImage("images/application_double.png"),
filterOptionsToolbarGroup, "1"));
filterDuplicates.setSelected(true);
filterDuplicates.setHelpLocation(
new HelpLocation(plugin.getName(), "Call_Tree_Action_Filter"));
filterDuplicates
.setHelpLocation(new HelpLocation(plugin.getName(), "Call_Tree_Action_Filter"));
tool.addLocalAction(this, filterDuplicates);
//
@ -393,8 +393,8 @@ public class CallTreeProvider extends ComponentProviderAdapter implements Domain
"<html>Recurse Depth<br><br>Limits the depth to " + "which recursing tree operations" +
"<br> will go. Example operations include <b>Expand All</b> and filtering");
recurseIcon = new NumberIcon(recurseDepth.get());
recurseDepthAction.setToolBarData(
new ToolBarData(recurseIcon, filterOptionsToolbarGroup, "2"));
recurseDepthAction
.setToolBarData(new ToolBarData(recurseIcon, filterOptionsToolbarGroup, "2"));
recurseDepthAction.setHelpLocation(
new HelpLocation(plugin.getName(), "Call_Tree_Action_Recurse_Depth"));
@ -414,8 +414,8 @@ public class CallTreeProvider extends ComponentProviderAdapter implements Domain
"Listing to<br>the <b>source</b> location of the call");
navigationOutgoingAction.setToolBarData(new ToolBarData(
Icons.NAVIGATE_ON_OUTGOING_EVENT_ICON, navigationOptionsToolbarGroup, "1"));
navigationOutgoingAction.setHelpLocation(
new HelpLocation(plugin.getName(), "Call_Tree_Action_Navigation"));
navigationOutgoingAction
.setHelpLocation(new HelpLocation(plugin.getName(), "Call_Tree_Action_Navigation"));
tool.addLocalAction(this, navigationOutgoingAction);
//
@ -596,8 +596,8 @@ public class CallTreeProvider extends ComponentProviderAdapter implements Domain
refreshAction.setEnabled(true);
refreshAction.setDescription("<html>Push at any time to refresh the current trees.<br>" +
"This is highlighted when the data <i>may</i> be stale.<br>");
refreshAction.setHelpLocation(
new HelpLocation(plugin.getName(), "Call_Tree_Action_Refresh"));
refreshAction
.setHelpLocation(new HelpLocation(plugin.getName(), "Call_Tree_Action_Refresh"));
tool.addLocalAction(this, refreshAction);
//
@ -671,8 +671,8 @@ public class CallTreeProvider extends ComponentProviderAdapter implements Domain
"Call_Tree_Context_Action_Show_Call_Tree_For_Function"));
newCallTree.setPopupMenuData(new MenuData(new String[] { "Show Call Tree For Function" },
CallTreePlugin.PROVIDER_ICON, newTreeMenu));
newCallTree.setDescription("Show the Function Call Tree window for the function " +
"selected in the call tree");
newCallTree.setDescription(
"Show the Function Call Tree window for the function " + "selected in the call tree");
tool.addLocalAction(this, newCallTree);
}

View File

@ -341,7 +341,7 @@ class ParseDialog extends DialogComponentProvider {
}
};
saveAction.setEnabled(false);
ImageIcon icon = ResourceManager.loadImage("images/disk.png");
Icon icon = ResourceManager.loadImage("images/disk.png");
String saveGroup = "save";
saveAction.setMenuBarData(new MenuData(new String[] { "Save" }, icon, saveGroup));
saveAction.setToolBarData(new ToolBarData(icon, saveGroup));
@ -371,8 +371,8 @@ class ParseDialog extends DialogComponentProvider {
clearAction.setEnabled(true);
icon = ResourceManager.loadImage("images/erase16.png");
String clearGroup = "clear";
clearAction.setMenuBarData(
new MenuData(new String[] { "Clear Profile" }, icon, clearGroup));
clearAction
.setMenuBarData(new MenuData(new String[] { "Clear Profile" }, icon, clearGroup));
clearAction.setToolBarData(new ToolBarData(icon, clearGroup));
clearAction.setDescription("Clear profile");
addAction(clearAction);

View File

@ -19,7 +19,7 @@ import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.util.List;
import javax.swing.ImageIcon;
import javax.swing.Icon;
import javax.swing.tree.TreePath;
import docking.ActionContext;
@ -40,7 +40,7 @@ import resources.Icons;
*/
public class CollapseAllArchivesAction extends DockingAction {
private ImageIcon collapseIcon = Icons.COLLAPSE_ALL_ICON;
private Icon collapseIcon = Icons.COLLAPSE_ALL_ICON;
private final DataTypeManagerPlugin plugin;
public CollapseAllArchivesAction(DataTypeManagerPlugin plugin) {

View File

@ -32,7 +32,7 @@ import docking.ActionContext;
import docking.ComponentProvider;
import docking.action.*;
import docking.actions.KeyBindingUtils;
import docking.options.editor.FontPropertyEditor;
import docking.options.editor.FontEditor;
import docking.widgets.OptionDialog;
import docking.widgets.filechooser.GhidraFileChooser;
import ghidra.framework.options.SaveState;
@ -287,7 +287,7 @@ public class TextEditorComponentProvider extends ComponentProviderAdapter {
}
protected void doSelectFont() {
FontPropertyEditor editor = new FontPropertyEditor();
FontEditor editor = new FontEditor();
editor.setValue(defaultFont);
editor.showDialog();
defaultFont = (Font) editor.getValue();

View File

@ -18,7 +18,7 @@ package ghidra.app.plugin.core.functioncompare.actions;
import java.awt.event.*;
import java.util.List;
import javax.swing.ImageIcon;
import javax.swing.Icon;
import docking.action.ToggleDockingAction;
import docking.action.ToolBarData;
@ -48,7 +48,7 @@ public class NavigateToFunctionAction extends ToggleDockingAction {
private GoToService goToService;
private static final ImageIcon NAV_FUNCTION_ICON = Icons.NAVIGATE_ON_INCOMING_EVENT_ICON;
private static final Icon NAV_FUNCTION_ICON = Icons.NAVIGATE_ON_INCOMING_EVENT_ICON;
/**
* Constructor
@ -64,10 +64,9 @@ public class NavigateToFunctionAction extends ToggleDockingAction {
setSelected(false);
ToolBarData newToolBarData = new ToolBarData(NAV_FUNCTION_ICON);
setToolBarData(newToolBarData);
setDescription(
HTMLUtilities.toHTML("Toggle <b>On</b> means to navigate to whatever " +
"function is selected in the comparison panel, when focus changes or" +
"a new function is selected."));
setDescription(HTMLUtilities.toHTML("Toggle <b>On</b> means to navigate to whatever " +
"function is selected in the comparison panel, when focus changes or" +
"a new function is selected."));
setHelpLocation(
new HelpLocation(MultiFunctionComparisonPanel.HELP_TOPIC, "Navigate_To_Function"));

View File

@ -17,7 +17,7 @@ package ghidra.app.plugin.core.interpreter;
import java.util.List;
import javax.swing.ImageIcon;
import javax.swing.Icon;
import ghidra.app.plugin.core.console.CodeCompletion;
@ -38,7 +38,7 @@ public interface InterpreterConnection {
*
* @return The icon associated with the interpreter. Null if default icon is desired.
*/
public ImageIcon getIcon();
public Icon getIcon();
/**
* Gets a {@link List} of {@link CodeCompletion code completions} for the given command.

View File

@ -30,7 +30,7 @@ import javax.swing.undo.UndoableEdit;
import docking.*;
import docking.action.*;
import docking.actions.KeyBindingUtils;
import docking.options.editor.FontPropertyEditor;
import docking.options.editor.FontEditor;
import docking.widgets.OptionDialog;
import generic.jar.ResourceFile;
import ghidra.app.script.GhidraScriptUtil;
@ -516,7 +516,7 @@ public class GhidraScriptEditorComponentProvider extends ComponentProvider {
}
private void doSelectFont() {
FontPropertyEditor editor = new FontPropertyEditor();
FontEditor editor = new FontEditor();
editor.setValue(defaultFont);
editor.showDialog();
defaultFont = (Font) editor.getValue();

View File

@ -39,7 +39,7 @@ class GhidraScriptTableModel extends GDynamicColumnTableModel<ResourceFile, Obje
static final String SCRIPT_STATUS_COLUMN_NAME = "Status";
private static final String EMPTY_STRING = "";
private static final ImageIcon ERROR_IMG = Icons.ERROR_ICON;
private static final Icon ERROR_IMG = Icons.ERROR_ICON;
private GhidraScriptComponentProvider provider;
private List<ResourceFile> scriptList = new ArrayList<>();
@ -217,8 +217,8 @@ class GhidraScriptTableModel extends GDynamicColumnTableModel<ResourceFile, Obje
Swing.runIfSwingOrRunLater(() -> super.fireTableChanged(e));
}
private class StatusColumn extends AbstractDynamicTableColumn<ResourceFile, ImageIcon, Object> {
private Comparator<ImageIcon> comparator = (i1, i2) -> {
private class StatusColumn extends AbstractDynamicTableColumn<ResourceFile, Icon, Object> {
private Comparator<Icon> comparator = (i1, i2) -> {
if (i1 == i2) {
return 0;
}
@ -234,12 +234,19 @@ class GhidraScriptTableModel extends GDynamicColumnTableModel<ResourceFile, Obje
if (i2 == null) {
return -1; // empty after icon
}
String d1 = i1.getDescription();
String d2 = i2.getDescription();
String d1 = getDescription(i1);
String d2 = getDescription(i2);
return SystemUtilities.compareTo(d1, d2);
};
private GColumnRenderer<ImageIcon> renderer = new AbstractGColumnRenderer<>() {
private String getDescription(Icon icon) {
if (icon instanceof ImageIcon imageIcon) {
return imageIcon.getDescription();
}
return icon.getClass().getName();
}
private GColumnRenderer<Icon> renderer = new AbstractGColumnRenderer<>() {
@Override
public Component getTableCellRendererComponent(GTableCellRenderingData data) {
JLabel label = (JLabel) super.getTableCellRendererComponent(data);
@ -269,14 +276,14 @@ class GhidraScriptTableModel extends GDynamicColumnTableModel<ResourceFile, Obje
}
@Override
public String getFilterString(ImageIcon t, Settings settings) {
public String getFilterString(Icon t, Settings settings) {
// we could use the tooltip text, but it doesn't seem worth it
return "";
}
};
@Override
public GColumnRenderer<ImageIcon> getColumnRenderer() {
public GColumnRenderer<Icon> getColumnRenderer() {
return renderer;
}
@ -286,7 +293,7 @@ class GhidraScriptTableModel extends GDynamicColumnTableModel<ResourceFile, Obje
}
@Override
public ImageIcon getValue(ResourceFile rowObject, Settings settings, Object data,
public Icon getValue(ResourceFile rowObject, Settings settings, Object data,
ServiceProvider sp) throws IllegalArgumentException {
ScriptInfo info = infoManager.getExistingScriptInfo(rowObject);
if (info.isCompileErrors() || info.isDuplicate()) {
@ -296,7 +303,7 @@ class GhidraScriptTableModel extends GDynamicColumnTableModel<ResourceFile, Obje
}
@Override
public Comparator<ImageIcon> getComparator() {
public Comparator<Icon> getComparator() {
return comparator;
}
}

View File

@ -18,7 +18,7 @@ package ghidra.app.plugin.core.symtable;
import java.awt.Cursor;
import java.awt.event.KeyEvent;
import javax.swing.ImageIcon;
import javax.swing.Icon;
import docking.ActionContext;
import docking.action.*;
@ -231,8 +231,8 @@ public class SymbolTablePlugin extends Plugin implements DomainObjectListener {
case ChangeManager.DOCR_CODE_ADDED:
case ChangeManager.DOCR_CODE_REMOVED:
if (rec.getNewValue() instanceof Data) {
domainObjectWorker.schedule(
new CodeAddedRemoveJob(currentProgram, rec.getStart()));
domainObjectWorker
.schedule(new CodeAddedRemoveJob(currentProgram, rec.getStart()));
}
break;
@ -241,16 +241,15 @@ public class SymbolTablePlugin extends Plugin implements DomainObjectListener {
// dynamic label symbols. Reference events must be used to check for existence
// of a dynamic label symbol.
Symbol symbol = (Symbol) rec.getNewValue();
domainObjectWorker.schedule(
new SymbolAddedJob(currentProgram, symbol));
domainObjectWorker.schedule(new SymbolAddedJob(currentProgram, symbol));
break;
case ChangeManager.DOCR_SYMBOL_REMOVED:
Address removeAddr = rec.getStart();
Long symbolID = (Long) rec.getNewValue();
domainObjectWorker.schedule(
new SymbolRemovedJob(currentProgram, removeAddr, symbolID));
domainObjectWorker
.schedule(new SymbolRemovedJob(currentProgram, removeAddr, symbolID));
break;
case ChangeManager.DOCR_SYMBOL_RENAMED:
@ -294,8 +293,8 @@ public class SymbolTablePlugin extends Plugin implements DomainObjectListener {
case ChangeManager.DOCR_EXTERNAL_ENTRY_POINT_REMOVED:
Address address = rec.getStart();
domainObjectWorker.schedule(
new ExternalEntryChangedJob(currentProgram, address));
domainObjectWorker
.schedule(new ExternalEntryChangedJob(currentProgram, address));
break;
}
}
@ -344,7 +343,7 @@ public class SymbolTablePlugin extends Plugin implements DomainObjectListener {
refProvider.setCurrentSymbol(symProvider.getCurrentSymbol());
}
};
ImageIcon icon = ResourceManager.loadImage("images/table_go.png");
Icon icon = ResourceManager.loadImage("images/table_go.png");
openRefsAction.setPopupMenuData(
new MenuData(new String[] { "Symbol References" }, icon, popupGroup));
openRefsAction.setToolBarData(new ToolBarData(icon));
@ -467,8 +466,8 @@ public class SymbolTablePlugin extends Plugin implements DomainObjectListener {
};
instructionsFromAction.setDescription("Instructions From");
instructionsFromAction.setSelected(false);
instructionsFromAction.setToolBarData(
new ToolBarData(ResourceManager.loadImage("images/I.gif"), null));
instructionsFromAction
.setToolBarData(new ToolBarData(ResourceManager.loadImage("images/I.gif"), null));
tool.addLocalAction(refProvider, instructionsFromAction);
@ -490,8 +489,8 @@ public class SymbolTablePlugin extends Plugin implements DomainObjectListener {
};
dataFromAction.setDescription("Data From");
dataFromAction.setSelected(false);
dataFromAction.setToolBarData(
new ToolBarData(ResourceManager.loadImage("images/D.gif"), null));
dataFromAction
.setToolBarData(new ToolBarData(ResourceManager.loadImage("images/D.gif"), null));
tool.addLocalAction(refProvider, dataFromAction);
}

View File

@ -15,7 +15,7 @@
*/
package ghidra.app.plugin.debug;
import javax.swing.ImageIcon;
import javax.swing.Icon;
import docking.ActionContext;
import docking.action.DockingAction;
@ -73,7 +73,7 @@ public class DbViewerPlugin extends Plugin {
};
refreshAction.setEnabled(false);
ImageIcon icon = Icons.REFRESH_ICON;
Icon icon = Icons.REFRESH_ICON;
refreshAction.setToolBarData(new ToolBarData(icon));
}

View File

@ -19,7 +19,7 @@ import docking.action.builder.ActionBuilder;
import docking.theme.gui.ThemeDialog;
import ghidra.app.CorePluginPackage;
import ghidra.app.plugin.PluginCategoryNames;
import ghidra.framework.main.FrontEndOnly;
import ghidra.framework.main.ApplicationLevelOnlyPlugin;
import ghidra.framework.main.FrontEndTool;
import ghidra.framework.plugintool.*;
import ghidra.framework.plugintool.util.PluginStatus;
@ -35,7 +35,7 @@ import ghidra.util.SystemUtilities;
"This plugin is available only in the Ghidra Project Window."
)
//@formatter:on
public class ThemeManagerPlugin extends Plugin implements FrontEndOnly {
public class ThemeManagerPlugin extends Plugin implements ApplicationLevelOnlyPlugin {
public ThemeManagerPlugin(PluginTool tool) {
super(tool);

View File

@ -15,7 +15,7 @@
*/
package ghidra.plugins.fsbrowser;
import javax.swing.ImageIcon;
import javax.swing.Icon;
import resources.Icons;
import resources.ResourceManager;
@ -28,43 +28,43 @@ import resources.ResourceManager;
*/
public class ImageManager {
//@formatter:off
public final static ImageIcon COPY = ResourceManager.loadImage("images/page_copy.png");
public final static ImageIcon CUT = ResourceManager.loadImage("images/edit-cut.png");
public final static ImageIcon DELETE = ResourceManager.loadImage("images/page_delete.png");
public final static ImageIcon FONT = ResourceManager.loadImage("images/text_lowercase.png");
public final static ImageIcon LOCKED = ResourceManager.loadImage("images/lock.gif");
public final static ImageIcon NEW = ResourceManager.loadImage("images/page_add.png");
public final static ImageIcon PASTE = ResourceManager.loadImage("images/page_paste.png");
public final static ImageIcon REDO = ResourceManager.loadImage("images/redo.png");
public final static ImageIcon RENAME = ResourceManager.loadImage("images/textfield_rename.png");
public final static ImageIcon REFRESH = Icons.REFRESH_ICON;
public final static ImageIcon SAVE = ResourceManager.loadImage("images/disk.png");
public final static ImageIcon SAVE_AS = ResourceManager.loadImage("images/disk_save_as.png");
public final static ImageIcon UNDO = ResourceManager.loadImage("images/undo.png");
public final static ImageIcon UNLOCKED = ResourceManager.loadImage("images/unlock.gif");
public final static ImageIcon CLOSE = ResourceManager.loadImage("images/famfamfam_silk_icons_v013/door.png");
public final static ImageIcon COLLAPSE_ALL = ResourceManager.loadImage("images/famfamfam_silk_icons_v013/arrow_in.png");
public final static ImageIcon COMPRESS = ResourceManager.loadImage("images/famfamfam_silk_icons_v013/compress.png");
public final static ImageIcon CREATE_FIRMWARE = ResourceManager.loadImage("images/media-flash.png");
public final static ImageIcon EXPAND_ALL = ResourceManager.loadImage("images/famfamfam_silk_icons_v013/arrow_inout.png");
public final static ImageIcon EXTRACT = ResourceManager.loadImage("images/package_green.png");
public final static ImageIcon INFO = ResourceManager.loadImage("images/famfamfam_silk_icons_v013/information.png");
public final static ImageIcon OPEN = ResourceManager.loadImage("images/famfamfam_silk_icons_v013/door_open.png");
public final static ImageIcon OPEN_AS_BINARY = ResourceManager.loadImage("images/famfamfam_silk_icons_v013/controller.png");
public final static ImageIcon OPEN_IN_LISTING = ResourceManager.loadImage("images/famfamfam_silk_icons_v013/folder_table.png");
public final static ImageIcon OPEN_FILE_SYSTEM = ResourceManager.loadImage("images/famfamfam_silk_icons_v013/folder_brick.png");
public final static ImageIcon PHOTO = ResourceManager.loadImage("images/famfamfam_silk_icons_v013/photo.png");
public final static ImageIcon VIEW_AS_IMAGE = ResourceManager.loadImage("images/oxygen/16x16/games-config-background.png");
public final static ImageIcon VIEW_AS_TEXT = ResourceManager.loadImage("images/format-text-bold.png");
public final static ImageIcon UNKNOWN = ResourceManager.loadImage("images/help-browser.png");
public final static ImageIcon IPOD = ResourceManager.loadImage("images/famfamfam_silk_icons_v013/ipod.png");
public final static ImageIcon IPOD_48 = ResourceManager.loadImage("images/oxygen/48x48/multimedia-player-apple-ipod.png");
public final static ImageIcon ECLIPSE = ResourceManager.loadImage("images/eclipse.png");
public final static ImageIcon JAR = ResourceManager.loadImage("images/famfamfam_silk_icons_v013/page_white_cup.png");
public final static ImageIcon KEY = ResourceManager.loadImage("images/famfamfam_silk_icons_v013/application_key.png");
public final static ImageIcon IMPORT = ResourceManager.loadImage("images/famfamfam_silk_icons_v013/application_get.png");
public final static ImageIcon iOS = ResourceManager.loadImage("images/famfamfam_silk_icons_v013/phone.png");
public final static ImageIcon OPEN_ALL = ResourceManager.loadImage("images/famfamfam_silk_icons_v013/application_cascade.png");
public final static ImageIcon LIST_MOUNTED = ResourceManager.loadImage("images/downArrow.png");
public final static Icon COPY = ResourceManager.loadImage("images/page_copy.png");
public final static Icon CUT = ResourceManager.loadImage("images/edit-cut.png");
public final static Icon DELETE = ResourceManager.loadImage("images/page_delete.png");
public final static Icon FONT = ResourceManager.loadImage("images/text_lowercase.png");
public final static Icon LOCKED = ResourceManager.loadImage("images/lock.gif");
public final static Icon NEW = ResourceManager.loadImage("images/page_add.png");
public final static Icon PASTE = ResourceManager.loadImage("images/page_paste.png");
public final static Icon REDO = ResourceManager.loadImage("images/redo.png");
public final static Icon RENAME = ResourceManager.loadImage("images/textfield_rename.png");
public final static Icon REFRESH = Icons.REFRESH_ICON;
public final static Icon SAVE = ResourceManager.loadImage("images/disk.png");
public final static Icon SAVE_AS = ResourceManager.loadImage("images/disk_save_as.png");
public final static Icon UNDO = ResourceManager.loadImage("images/undo.png");
public final static Icon UNLOCKED = ResourceManager.loadImage("images/unlock.gif");
public final static Icon CLOSE = ResourceManager.loadImage("images/famfamfam_silk_icons_v013/door.png");
public final static Icon COLLAPSE_ALL = ResourceManager.loadImage("images/famfamfam_silk_icons_v013/arrow_in.png");
public final static Icon COMPRESS = ResourceManager.loadImage("images/famfamfam_silk_icons_v013/compress.png");
public final static Icon CREATE_FIRMWARE = ResourceManager.loadImage("images/media-flash.png");
public final static Icon EXPAND_ALL = ResourceManager.loadImage("images/famfamfam_silk_icons_v013/arrow_inout.png");
public final static Icon EXTRACT = ResourceManager.loadImage("images/package_green.png");
public final static Icon INFO = ResourceManager.loadImage("images/famfamfam_silk_icons_v013/information.png");
public final static Icon OPEN = ResourceManager.loadImage("images/famfamfam_silk_icons_v013/door_open.png");
public final static Icon OPEN_AS_BINARY = ResourceManager.loadImage("images/famfamfam_silk_icons_v013/controller.png");
public final static Icon OPEN_IN_LISTING = ResourceManager.loadImage("images/famfamfam_silk_icons_v013/folder_table.png");
public final static Icon OPEN_FILE_SYSTEM = ResourceManager.loadImage("images/famfamfam_silk_icons_v013/folder_brick.png");
public final static Icon PHOTO = ResourceManager.loadImage("images/famfamfam_silk_icons_v013/photo.png");
public final static Icon VIEW_AS_IMAGE = ResourceManager.loadImage("images/oxygen/16x16/games-config-background.png");
public final static Icon VIEW_AS_TEXT = ResourceManager.loadImage("images/format-text-bold.png");
public final static Icon UNKNOWN = ResourceManager.loadImage("images/help-browser.png");
public final static Icon IPOD = ResourceManager.loadImage("images/famfamfam_silk_icons_v013/ipod.png");
public final static Icon IPOD_48 = ResourceManager.loadImage("images/oxygen/48x48/multimedia-player-apple-ipod.png");
public final static Icon ECLIPSE = ResourceManager.loadImage("images/eclipse.png");
public final static Icon JAR = ResourceManager.loadImage("images/famfamfam_silk_icons_v013/page_white_cup.png");
public final static Icon KEY = ResourceManager.loadImage("images/famfamfam_silk_icons_v013/application_key.png");
public final static Icon IMPORT = ResourceManager.loadImage("images/famfamfam_silk_icons_v013/application_get.png");
public final static Icon iOS = ResourceManager.loadImage("images/famfamfam_silk_icons_v013/phone.png");
public final static Icon OPEN_ALL = ResourceManager.loadImage("images/famfamfam_silk_icons_v013/application_cascade.png");
public final static Icon LIST_MOUNTED = ResourceManager.loadImage("images/downArrow.png");
//@formatter:on
}

View File

@ -201,7 +201,7 @@ public class InterpreterPanelTest extends AbstractGhidraHeadedIntegrationTest {
}
@Override
public ImageIcon getIcon() {
public Icon getIcon() {
return Icons.STOP_ICON;
}
@ -222,8 +222,7 @@ public class InterpreterPanelTest extends AbstractGhidraHeadedIntegrationTest {
return result;
}
private void doSwingMultilinePasteTest(List<String> multiLineTestValues)
throws IOException {
private void doSwingMultilinePasteTest(List<String> multiLineTestValues) throws IOException {
// simulates what happens during a paste when multi-line string with
// "\n"s is pasted.
runSwingLater(() -> {

View File

@ -107,13 +107,10 @@ class SymbolServerPanel extends JPanel {
JPanel buttonPanel = buildButtonPanel();
JScrollPane tableScrollPane = buildTable();
defaultConfigNotice = new JPanel();
defaultConfigNotice.add(
new GHtmlLabel(
"<html><center><font color=red><br>" +
"Missing / invalid configuration.<br><br>" +
"Using default search location:<br>" +
"Program's Import Location<br>",
SwingConstants.CENTER));
defaultConfigNotice.add(new GHtmlLabel(
"<html><center><font color=red><br>" + "Missing / invalid configuration.<br><br>" +
"Using default search location:<br>" + "Program's Import Location<br>",
SwingConstants.CENTER));
defaultConfigNotice.setPreferredSize(tableScrollPane.getPreferredSize());
additionalSearchLocationsPanel = new JPanel();
@ -199,55 +196,47 @@ class SymbolServerPanel extends JPanel {
}
private JPanel buildButtonPanel() {
refreshSearchLocationsStatusButton = createImageButton(Icons.REFRESH_ICON, "Refresh Status",
ButtonPanelFactory.ARROW_SIZE);
refreshSearchLocationsStatusButton =
createImageButton(Icons.REFRESH_ICON, "Refresh Status", ButtonPanelFactory.ARROW_SIZE);
refreshSearchLocationsStatusButton.addActionListener(e -> refreshSearchLocationStatus());
DockingWindowManager.getHelpService()
.registerHelp(refreshSearchLocationsStatusButton,
new HelpLocation(PdbPlugin.PDB_PLUGIN_HELP_TOPIC,
"SymbolServerConfig Refresh Status"));
.registerHelp(refreshSearchLocationsStatusButton, new HelpLocation(
PdbPlugin.PDB_PLUGIN_HELP_TOPIC, "SymbolServerConfig Refresh Status"));
moveLocationUpButton = ButtonPanelFactory.createButton(ButtonPanelFactory.ARROW_UP_TYPE);
moveLocationUpButton.addActionListener(e -> moveLocation(-1));
moveLocationUpButton.setToolTipText("Move location up");
DockingWindowManager.getHelpService()
.registerHelp(moveLocationUpButton,
new HelpLocation(PdbPlugin.PDB_PLUGIN_HELP_TOPIC,
"SymbolServerConfig MoveUpDown"));
.registerHelp(moveLocationUpButton, new HelpLocation(
PdbPlugin.PDB_PLUGIN_HELP_TOPIC, "SymbolServerConfig MoveUpDown"));
moveLocationDownButton =
ButtonPanelFactory.createButton(ButtonPanelFactory.ARROW_DOWN_TYPE);
moveLocationDownButton.addActionListener(e -> moveLocation(1));
moveLocationDownButton.setToolTipText("Move location down");
DockingWindowManager.getHelpService()
.registerHelp(moveLocationDownButton,
new HelpLocation(PdbPlugin.PDB_PLUGIN_HELP_TOPIC,
"SymbolServerConfig MoveUpDown"));
.registerHelp(moveLocationDownButton, new HelpLocation(
PdbPlugin.PDB_PLUGIN_HELP_TOPIC, "SymbolServerConfig MoveUpDown"));
deleteLocationButton = createImageButton(Icons.DELETE_ICON, "Delete",
ButtonPanelFactory.ARROW_SIZE);
deleteLocationButton =
createImageButton(Icons.DELETE_ICON, "Delete", ButtonPanelFactory.ARROW_SIZE);
deleteLocationButton.addActionListener(e -> deleteLocation());
DockingWindowManager.getHelpService()
.registerHelp(deleteLocationButton,
new HelpLocation(PdbPlugin.PDB_PLUGIN_HELP_TOPIC,
"SymbolServerConfig Delete"));
new HelpLocation(PdbPlugin.PDB_PLUGIN_HELP_TOPIC, "SymbolServerConfig Delete"));
addLocationButton = createImageButton(Icons.ADD_ICON, "Add",
ButtonPanelFactory.ARROW_SIZE);
addLocationButton = createImageButton(Icons.ADD_ICON, "Add", ButtonPanelFactory.ARROW_SIZE);
addLocationButton.addActionListener(e -> addLocation());
DockingWindowManager.getHelpService()
.registerHelp(addLocationButton,
new HelpLocation(PdbPlugin.PDB_PLUGIN_HELP_TOPIC,
"SymbolServerConfig Add"));
new HelpLocation(PdbPlugin.PDB_PLUGIN_HELP_TOPIC, "SymbolServerConfig Add"));
saveSearchLocationsButton =
ButtonPanelFactory.createImageButton(Icons.get("images/disk.png"),
"Save Configuration", ButtonPanelFactory.ARROW_SIZE);
saveSearchLocationsButton = ButtonPanelFactory.createImageButton(
Icons.get("images/disk.png"), "Save Configuration", ButtonPanelFactory.ARROW_SIZE);
saveSearchLocationsButton.addActionListener(e -> saveConfig());
DockingWindowManager.getHelpService()
.registerHelp(saveSearchLocationsButton,
new HelpLocation(PdbPlugin.PDB_PLUGIN_HELP_TOPIC,
"SymbolServerConfig Save"));
new HelpLocation(PdbPlugin.PDB_PLUGIN_HELP_TOPIC, "SymbolServerConfig Save"));
JPanel buttonPanel = new JPanel();
buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.X_AXIS));
@ -325,8 +314,7 @@ class SymbolServerPanel extends JPanel {
}
if (allowGUIPrompt && isEmptyDirectory(symbolStorageDir)) {
if (OptionDialog.showYesNoDialog(this,
"Initialize Symbol Storage Directory?",
if (OptionDialog.showYesNoDialog(this, "Initialize Symbol Storage Directory?",
"<html>Initialize new directory as Microsoft symbol storage directory?") == OptionDialog.YES_OPTION) {
try {
LocalSymbolStore.create(symbolStorageDir,
@ -495,8 +483,8 @@ class SymbolServerPanel extends JPanel {
}
private void addDirectoryLocation() {
File dir = FilePromptDialog.chooseDirectory("Enter Path", "Symbol Storage Location: ",
null);
File dir =
FilePromptDialog.chooseDirectory("Enter Path", "Symbol Storage Location: ", null);
if (dir == null) {
return;
}
@ -574,7 +562,7 @@ class SymbolServerPanel extends JPanel {
return false;
}
private static JButton createImageButton(ImageIcon buttonIcon, String alternateText,
private static JButton createImageButton(Icon buttonIcon, String alternateText,
Dimension preferredSize) {
JButton button = ButtonPanelFactory.createButton("");
@ -584,7 +572,7 @@ class SymbolServerPanel extends JPanel {
return button;
}
static StatusText getSymbolServerWarnings(List<SymbolServer> symbolServers) {
Map<String, String> warningsByLocation = new HashMap<>();
for (WellKnownSymbolServerLocation ssloc : knownSymbolServers) {
@ -592,8 +580,7 @@ class SymbolServerPanel extends JPanel {
warningsByLocation.put(ssloc.getLocation(), ssloc.getWarning());
}
}
String warning = symbolServers
.stream()
String warning = symbolServers.stream()
.map(symbolServer -> warningsByLocation.get(symbolServer.getName()))
.filter(Objects::nonNull)
.distinct()

View File

@ -52,8 +52,8 @@ public class DiffDetailsProvider extends ComponentProviderAdapter {
public static final String FILTER_DIFFS_CHECK_BOX = "Filter Diffs Check Box";
public static final String DIFF_DETAILS_TEXT_AREA = "Diff Details Text Area";
public static final String DIFF_DETAILS_PANEL = "Diff Location Details Panel";
public static final ImageIcon ICON = ResourceManager.loadImage("images/xmag.png");
public static final ImageIcon REFRESH_ICON = Icons.REFRESH_ICON;
public static final Icon ICON = ResourceManager.loadImage("images/xmag.png");
public static final Icon REFRESH_ICON = Icons.REFRESH_ICON;
public static final String TITLE = "Diff Details";
private ProgramDiffPlugin plugin;
@ -121,8 +121,8 @@ public class DiffDetailsProvider extends ComponentProviderAdapter {
refreshDetailsAction.setEnabled(true);
refreshDetailsAction.setToolBarData(new ToolBarData(REFRESH_ICON, "Diff"));
refreshDetailsAction.setHelpLocation(
new HelpLocation(HelpTopics.DIFF, "Refresh Diff Details"));
refreshDetailsAction
.setHelpLocation(new HelpLocation(HelpTopics.DIFF, "Refresh Diff Details"));
plugin.getTool().addLocalAction(this, refreshDetailsAction);
// plugin.getTool().addLocalAction(this, new DiffIgnoreAllAction(this));
}

View File

@ -43,6 +43,34 @@ color.bg.fieldpanel.selection = color.bg.selection
color.bg.fieldpanel.highlight = color.bg.highlight
color.bg.fieldpanel.selection-highlight = green
// Icons file
icon.empty = images/EmptyIcon16.gif
icon.help = images/help-browser.png
icon.add = images/Plus2.png
icon.collapse.all = images/collapse_all.png
icon.expand.all = images/expand_all.png
icon.configure.filter = images/exec.png
icon.delete = images/error.png
icon.error = images/emblem-important.png
icon.navigate.in = images/locationIn.gif
icon.navigate.out = images/locationOut.gif
icon.notallowed = images/dialog-cancel.png
icon.folder.open = images/openSmallFolder.png
icon.refresh = images/reload3.png
icon.sort.ascending = images/sortascending.png
icon.sort.descending = images/process-stop.png
icon.stop = images/process-stop.png
icon.warning.strong = images/software-update-urgent.png
icon.left = images/left.png
icon.right = images/right.png
icon.left.alt = images/left.alternate.png
icon.right.alt = images/right.alternate.png
icon.saveas = images/disk.png
icon.makeselection = images/text_align_justify.png
icon.arrow.up.right = images/viewmagfit.png
icon.flag = images/flag.png
icon.lock = images/kgpg.png
icon.checkmark.green = images/checkmark_green.gif
[Dark Defaults]

View File

@ -29,7 +29,9 @@ import docking.DockingWindowManager;
import docking.widgets.label.GDHtmlLabel;
/**
* Color editor that uses the JColorChooser.
* Color editor that is a bit unusual in that its custom component is a button that when pushed,
* pops up a dialog for editing the color. Use {@link ColorPropertyEditor} for a more traditional
* property editor that returns a direct color editing component.
*/
public class ColorEditor extends PropertyEditorSupport {
@ -42,10 +44,6 @@ public class ColorEditor extends PropertyEditorSupport {
private Color color;
private Color lastUserSelectedColor;
/**
* The default constructor.
*
*/
public ColorEditor() {
previewLabel.setOpaque(true);
previewLabel.setPreferredSize(new Dimension(100, 20));
@ -72,42 +70,16 @@ public class ColorEditor extends PropertyEditorSupport {
DockingWindowManager.showDialog(previewLabel, provider);
}
/**
* A PropertyEditor may chose to make available a full custom Component
* that edits its property value. It is the responsibility of the
* PropertyEditor to hook itself up to its editor Component itself and
* to report property value changes by firing a PropertyChange event.
* <P>
* The higher-level code that calls getCustomEditor may either embed
* the Component in some larger property sheet, or it may put it in
* its own individual dialog, or ...
*
* @return A java.awt.Component that will allow a human to directly
* edit the current property value. May be null if this is
* not supported.
*/
@Override
public Component getCustomEditor() {
return previewLabel;
}
/**
* Determines whether the propertyEditor can provide a custom editor.
*
* @return True if the propertyEditor can provide a custom editor.
*/
@Override
public boolean supportsCustomEditor() {
return true;
}
/**
* Set (or change) the object that is to be edited.
* @param value The new target object to be edited. Note that this
* object should not be modified by the PropertyEditor, rather
* the PropertyEditor should create a new object to hold any
* modified value.
*/
@Override
public void setValue(Object value) {
color = (Color) value;
@ -130,33 +102,16 @@ public class ColorEditor extends PropertyEditorSupport {
previewLabel.setBackground(color);
}
/**
* Get the value.
*/
@Override
public Object getValue() {
return color;
}
/**
* Return true which this editor can paint its property value.
*/
@Override
public boolean isPaintable() {
return false;
}
/**
* Paint a representation of the value into a given area of screen
* real estate. Note that the propertyEditor is responsible for doing
* its own clipping so that it fits into the given rectangle.
* <p>
* If the PropertyEditor doesn't honor paint requests (see isPaintable)
* this method should be a silent noop.
*
* @param gfx Graphics object to paint into.
* @param box Rectangle within graphics object into which we should paint.
*/
@Override
public void paintValue(Graphics gfx, Rectangle box) {
if (color != null) {

View File

@ -0,0 +1,57 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package docking.options.editor;
import java.awt.Color;
import java.awt.Component;
import java.beans.PropertyEditorSupport;
import java.util.Objects;
import ghidra.util.Swing;
/**
* Property Editor for Colors. Uses a {@link GhidraColorChooser} as its custom component
*/
public class ColorPropertyEditor extends PropertyEditorSupport {
private GhidraColorChooser colorChooser;
private void colorChanged() {
// run later - allows debugging without hanging amazon aws
Swing.runLater(() -> setValue(colorChooser.getColor()));
}
@Override
public Component getCustomEditor() {
colorChooser = new GhidraColorChooser();
colorChooser.getSelectionModel().addChangeListener(e -> colorChanged());
return colorChooser;
}
@Override
public boolean supportsCustomEditor() {
return true;
}
@Override
public void setValue(Object value) {
if (colorChooser != null) {
colorChooser.setColor((Color) value);
}
if (!Objects.equals(value, getValue())) {
super.setValue(value);
}
}
}

View File

@ -1,6 +1,5 @@
/* ###
* 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.
@ -31,7 +30,7 @@ public class EditorInitializer implements ModuleInitializer {
public void run() {
PropertyEditorManager.registerEditor(String.class, StringEditor.class);
PropertyEditorManager.registerEditor(Color.class, ColorEditor.class);
PropertyEditorManager.registerEditor(Font.class, FontPropertyEditor.class);
PropertyEditorManager.registerEditor(Font.class, FontEditor.class);
PropertyEditorManager.registerEditor(Enum.class, EnumEditor.class);
PropertyEditorManager.registerEditor(Boolean.class, BooleanEditor.class);
PropertyEditorManager.registerEditor(Date.class, DateEditor.class);

View File

@ -0,0 +1,111 @@
/* ###
* 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.options.editor;
import java.awt.*;
import java.beans.PropertyEditorSupport;
import java.util.Objects;
import javax.swing.*;
import docking.DialogComponentProvider;
import docking.DockingWindowManager;
/**
* Font property editor that is a bit unusual in that its custom component is a button that when
* pushed, pops up a dialog for editing the color. Use {@link FontPropertyEditor} for a more
* traditional property editor that returns a direct color editing component.
*/
public class FontEditor extends PropertyEditorSupport {
private JButton previewButton;
private FontPropertyEditor fontPropertyEditor;
public FontEditor() {
previewButton = new JButton(FontPropertyEditor.SAMPLE_STRING);
previewButton.addActionListener(e -> buttonPushed());
fontPropertyEditor = new FontPropertyEditor();
fontPropertyEditor.addPropertyChangeListener(ev -> fontChanged());
}
private void buttonPushed() {
showDialog();
previewButton.setFont((Font) getValue());
}
/**
* Convenience method for directly showing a dialog for editing fonts
*/
public void showDialog() {
EditorDialogProvider provider = new EditorDialogProvider();
DockingWindowManager.showDialog(previewButton, provider);
previewButton.repaint();
}
@Override
public void setValue(Object o) {
if (Objects.equals(o, getValue())) {
return;
}
Font font = (Font) o;
previewButton.setFont(font);
fontPropertyEditor.setValue(font);
super.setValue(font);
}
@Override
public boolean supportsCustomEditor() {
return true;
}
@Override
public Component getCustomEditor() {
return previewButton;
}
private void fontChanged() {
Font font = (Font) fontPropertyEditor.getValue();
setValue(font);
}
class EditorDialogProvider extends DialogComponentProvider {
private Font originalFont = (Font) getValue();
EditorDialogProvider() {
super("Font Editor", true);
addWorkPanel(buildWorkPanel());
addOKButton();
addCancelButton();
}
private JComponent buildWorkPanel() {
JPanel panel = new JPanel(new BorderLayout());
panel.add(fontPropertyEditor.getCustomEditor());
return panel;
}
@Override
protected void okCallback() {
close();
}
@Override
protected void cancelCallback() {
setValue(originalFont);
close();
}
}
}

View File

@ -16,74 +16,33 @@
package docking.options.editor;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyEditorSupport;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Arrays;
import java.util.Objects;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import javax.swing.*;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import docking.DialogComponentProvider;
import docking.DockingWindowManager;
import docking.widgets.combobox.GComboBox;
import docking.widgets.label.GDLabel;
import ghidra.util.Swing;
/**
* This Bean FontEditor displays a String with the current selected font name,
* style and size attributes.
* Property Editor for editing {@link Font}s
*/
public class FontPropertyEditor extends PropertyEditorSupport {
private Font font;
// private JLabel previewLabel = new GDLabel();
private final static String SAMPLE_STRING = "ABCabc \u00a9\u00ab\u00a7\u0429\u05d1\u062c\u4eb9";
private JButton previewButton = new JButton(SAMPLE_STRING);
public final static String SAMPLE_STRING = "ABCabc \u00a9\u00ab\u00a7\u0429\u05d1\u062c\u4eb9";
/**
* The default constructor.
*
*/
public FontPropertyEditor() {
previewButton.addActionListener(e -> {
// show the editor to get the user value
showDialog();
// now set the new value
previewButton.setFont(font);
});
// previewLabel.addMouseListener( new MouseAdapter() {
// @Override
// public void mouseClicked( MouseEvent evt ) {
// // show the editor to get the user value
// showDialog();
//
// // now set the new value
// previewLabel.setFont( font );
// }
// } );
}
public void showDialog() {
EditorProvider provider = new EditorProvider(new FontPanel());
DockingWindowManager.showDialog(previewButton, provider);
previewButton.repaint();
}
private FontChooserPanel fontChooserPanel;
@Override
public void setValue(Object o) {
font = (Font) o;
previewButton.setFont(font);
// set the font values on the widget
}
@Override
public Object getValue() {
return font;
public Component getCustomEditor() {
fontChooserPanel = new FontChooserPanel();
fontChooserPanel.updateControls((Font) getValue());
return fontChooserPanel;
}
@Override
@ -92,205 +51,204 @@ public class FontPropertyEditor extends PropertyEditorSupport {
}
@Override
public Component getCustomEditor() {
return previewButton;
public void setValue(Object value) {
if (fontChooserPanel != null) {
fontChooserPanel.updateControls((Font) value);
}
if (!Objects.equals(value, getValue())) {
super.setValue(value);
}
}
//==================================================================================================
// Inner Classes
//==================================================================================================
class FontChooserPanel extends JPanel {
private class FontPanel extends JPanel implements ActionListener {
JLabel fontLabel, sizeLabel, styleLabel;
JLabel fontStringLabel;
JComboBox<FontWrapper> fonts;
JComboBox<Integer> sizes;
JComboBox<String> styles;
int styleChoice;
int sizeChoice;
private GDLabel previewLabel;
private GComboBox<FontWrapper> fontCombo;
private GComboBox<Integer> sizeCombo;
private GComboBox<String> styleCombo;
private ActionListener actionListener = e -> fontChanged();
FontPanel() {
init();
public FontChooserPanel() {
build();
}
public void init() {
this.setLayout(new BorderLayout());
public void updateControls(Font font) {
if (font == null) {
return;
}
updatePreviewLabel(font);
JPanel topPanel = new JPanel();
JPanel fontPanel = new JPanel();
JPanel sizePanel = new JPanel();
JPanel stylePanel = new JPanel();
JPanel sizeAndStylePanel = new JPanel();
fontCombo.removeActionListener(actionListener);
sizeCombo.removeActionListener(actionListener);
styleCombo.removeActionListener(actionListener);
topPanel.setLayout(new BorderLayout());
fontPanel.setLayout(new GridLayout(2, 1));
sizePanel.setLayout(new GridLayout(2, 1));
stylePanel.setLayout(new GridLayout(2, 1));
sizeAndStylePanel.setLayout(new BorderLayout());
FontWrapper fontWrapper = new FontWrapper(font.getName());
int styleChoice = font.getStyle();
int size = font.getSize();
topPanel.add(BorderLayout.WEST, fontPanel);
sizeAndStylePanel.add(BorderLayout.WEST, sizePanel);
sizeAndStylePanel.add(BorderLayout.CENTER, stylePanel);
topPanel.add(BorderLayout.CENTER, sizeAndStylePanel);
fontCombo.setSelectedItem(fontWrapper);
sizeCombo.setSelectedItem(size);
styleCombo.setSelectedIndex(styleChoice);
fontStringLabel = new GDLabel(FontPropertyEditor.SAMPLE_STRING);
fontStringLabel.setPreferredSize(new Dimension(350, 50));
fontStringLabel.setHorizontalAlignment(SwingConstants.CENTER);
fontStringLabel.setFont(font);
topPanel.add(BorderLayout.SOUTH, fontStringLabel);
fontCombo.addActionListener(actionListener);
sizeCombo.addActionListener(actionListener);
styleCombo.addActionListener(actionListener);
add(BorderLayout.NORTH, topPanel);
}
fontLabel = new GDLabel("Fonts");
Font newFont = getFont().deriveFont(1);
fontLabel.setFont(newFont);
fontLabel.setHorizontalAlignment(SwingConstants.CENTER);
fontPanel.add(fontLabel);
private void build() {
setLayout(new BorderLayout());
add(buildTopPanel(), BorderLayout.NORTH);
add(buildPreviewLabel(), BorderLayout.CENTER);
}
sizeLabel = new GDLabel("Sizes");
sizeLabel.setFont(newFont);
sizeLabel.setHorizontalAlignment(SwingConstants.CENTER);
sizePanel.add(sizeLabel);
private Component buildTopPanel() {
JPanel panel = new JPanel(new FlowLayout(SwingConstants.CENTER, 10, 0));
panel.add(buildFontPanel());
panel.add(buildSizePanel());
panel.add(buildStylePanel());
return panel;
}
styleLabel = new GDLabel("Styles");
styleLabel.setFont(newFont);
private Component buildPreviewLabel() {
previewLabel = new GDLabel(SAMPLE_STRING);
previewLabel.setPreferredSize(new Dimension(350, 50));
previewLabel.setHorizontalAlignment(SwingConstants.CENTER);
previewLabel.setVerticalAlignment(SwingConstants.CENTER);
previewLabel.setMinimumSize(new Dimension(300, 50));
return previewLabel;
}
private Component buildStylePanel() {
JPanel panel = new JPanel(new GridLayout(2, 1));
GDLabel styleLabel = new GDLabel("Styles");
styleLabel.setFont(getFont().deriveFont(1));
styleLabel.setHorizontalAlignment(SwingConstants.CENTER);
stylePanel.add(styleLabel);
panel.add(styleLabel);
styleCombo =
new GComboBox<>(new String[] { "PLAIN", "BOLD", "ITALIC", "BOLD & ITALIC" });
styleCombo.setMaximumRowCount(9);
styleCombo.addActionListener(actionListener);
panel.add(styleCombo);
return panel;
}
private Component buildSizePanel() {
JPanel panel = new JPanel(new GridLayout(2, 1));
GDLabel sizeLabel = new GDLabel("Sizes");
sizeLabel.setFont(getFont().deriveFont(1));
sizeLabel.setHorizontalAlignment(SwingConstants.CENTER);
panel.add(sizeLabel);
sizeCombo =
new GComboBox<>(IntStream.rangeClosed(1, 72).boxed().toArray(Integer[]::new));
sizeCombo.setMaximumRowCount(9);
sizeCombo.setMaximumRowCount(9);
sizeCombo.addActionListener(actionListener);
panel.add(sizeCombo);
return panel;
}
private Component buildFontPanel() {
JPanel panel = new JPanel(new GridLayout(2, 1));
GDLabel fontLabel = new GDLabel("Fonts");
fontLabel.setFont(getFont().deriveFont(1));
fontLabel.setHorizontalAlignment(SwingConstants.CENTER);
panel.add(fontLabel);
GraphicsEnvironment gEnv = GraphicsEnvironment.getLocalGraphicsEnvironment();
Stream<String> stream = Arrays.stream(gEnv.getAvailableFontFamilyNames());
FontWrapper[] array = stream.map(s -> new FontWrapper(s)).toArray(FontWrapper[]::new);
Arrays.sort(array);
String envfonts[] = gEnv.getAvailableFontFamilyNames();
List<FontWrapper> list = new ArrayList<>(envfonts.length);
for (String envfont : envfonts) {
list.add(new FontWrapper(envfont));
}
Collections.sort(list);
fonts = new GComboBox<>(list.toArray(new FontWrapper[envfonts.length]));
fonts.setMaximumRowCount(9);
FontWrapper fontWrapper = new FontWrapper(font.getName());
fontPanel.add(fonts);
fonts.setSelectedItem(fontWrapper);
fontCombo = new GComboBox<>(array);
fontCombo.setMaximumRowCount(9);
fontCombo.addActionListener(actionListener);
panel.add(fontCombo);
sizes = new GComboBox<>(IntStream.rangeClosed(1, 72).boxed().toArray(Integer[]::new));
sizes.setMaximumRowCount(9);
sizePanel.add(sizes);
sizeChoice = font.getSize();
sizes.setSelectedItem(sizeChoice);
sizes.setMaximumRowCount(9);
styles = new GComboBox<>(new String[] { "PLAIN", "BOLD", "ITALIC", "BOLD & ITALIC" });
styles.setMaximumRowCount(9);
stylePanel.add(styles);
styleChoice = font.getStyle();
styles.setSelectedIndex(styleChoice);
fonts.addActionListener(this);
styles.addActionListener(this);
sizes.addActionListener(this);
return panel;
}
@Override
public void actionPerformed(ActionEvent event) {
// get values of panels
// set the editors new font
Object list = event.getSource();
private void fontChanged() {
FontWrapper fontWrapper = (FontWrapper) fontCombo.getSelectedItem();
String fontNameChoice = fontWrapper.getFontName();
int styleChoice = styleCombo.getSelectedIndex();
int sizeChoice = (Integer) sizeCombo.getSelectedItem();
Font font = new Font(fontNameChoice, styleChoice, sizeChoice);
updatePreviewLabel(font);
// allows debugging without hanging amazon aws
Swing.runLater(() -> setValue(font));
}
String fontNameChoice = font.getName();
if (list == fonts) {
FontWrapper fontWrapper = (FontWrapper) fonts.getSelectedItem();
fontNameChoice = fontWrapper.getFontName();
}
else if (list == styles) {
styleChoice = styles.getSelectedIndex();
}
else {
sizeChoice = (Integer) sizes.getSelectedItem();
}
font = new Font(fontNameChoice, styleChoice, sizeChoice);
fontStringLabel.setFont(font);
FontMetrics fm = fontStringLabel.getFontMetrics(font);
private void updatePreviewLabel(Font font) {
previewLabel.setFont(font);
FontMetrics fm = previewLabel.getFontMetrics(font);
int height = fm.getHeight();
Dimension d = fontStringLabel.getSize();
Dimension d = previewLabel.getSize();
if (d.height < height) {
d = new Dimension(d.width, height);
fontStringLabel.setPreferredSize(d);
previewLabel.setPreferredSize(d);
}
fontStringLabel.invalidate();
previewLabel.invalidate();
setValue(font);
FontPropertyEditor.this.firePropertyChange();
}
// A wrapper class created so that the names of fonts are comparable ignoring case
private class FontWrapper implements Comparable<FontWrapper> {
private final String fontName;
private FontWrapper(String fontName) {
this.fontName = fontName;
}
private String getFontName() {
return fontName;
}
@Override
public String toString() {
return fontName;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!getClass().equals(obj.getClass())) {
return false;
}
FontWrapper otherWrapper = (FontWrapper) obj;
return fontName.toLowerCase().equals(otherWrapper.fontName.toLowerCase());
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result =
prime * result + ((fontName == null) ? 0 : fontName.toLowerCase().hashCode());
return result;
}
@Override
public int compareTo(FontWrapper otherWrapper) {
return fontName.compareToIgnoreCase(otherWrapper.fontName);
}
}
}
class EditorProvider extends DialogComponentProvider {
private Font originalFont = font;
EditorProvider(JPanel contentPanel) {
super("Font Editor", true);
addWorkPanel(contentPanel);
addOKButton();
addCancelButton();
}
@Override
protected void okCallback() {
close();
}
@Override
protected void cancelCallback() {
font = originalFont;
super.cancelCallback();
}
}
// A wrapper class created so that the names of fonts are comparable ignoring case
private class FontWrapper implements Comparable<FontWrapper> {
private final String fontName;
private FontWrapper(String fontName) {
this.fontName = fontName;
}
private String getFontName() {
return fontName;
}
@Override
public String toString() {
return fontName;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!getClass().equals(obj.getClass())) {
return false;
}
FontWrapper otherWrapper = (FontWrapper) obj;
return fontName.toLowerCase().equals(otherWrapper.fontName.toLowerCase());
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((fontName == null) ? 0 : fontName.toLowerCase().hashCode());
return result;
}
@Override
public int compareTo(FontWrapper otherWrapper) {
return fontName.compareToIgnoreCase(otherWrapper.fontName);
}
}
}

View File

@ -0,0 +1,249 @@
/* ###
* 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.options.editor;
import java.awt.*;
import java.beans.PropertyEditorSupport;
import java.util.*;
import java.util.List;
import javax.swing.*;
import docking.theme.gui.ProtectedIcon;
import docking.widgets.*;
import docking.widgets.label.GDLabel;
import docking.widgets.list.GListCellRenderer;
import resources.ResourceManager;
import resources.icons.ScaledImageIcon;
import resources.icons.UrlImageIcon;
public class IconPropertyEditor extends PropertyEditorSupport {
private IconChooserPanel iconChooserPanel;
@Override
public Component getCustomEditor() {
iconChooserPanel = new IconChooserPanel();
return iconChooserPanel;
}
@Override
public boolean supportsCustomEditor() {
return true;
}
@Override
public void setValue(Object value) {
if (iconChooserPanel != null) {
iconChooserPanel.setSelectedIcon((Icon) value);
}
doSetValue(value);
}
private void doSetValue(Object value) {
if (!Objects.equals(value, getValue())) {
super.setValue(value);
}
}
private String iconToString(Icon icon) {
if (icon instanceof UrlImageIcon urlIcon) {
return urlIcon.getOriginalPath();
}
return "<Default>";
}
class IconChooserPanel extends JPanel {
private GDLabel previewLabel;
private DropDownSelectionTextField<Icon> dropDown;
private IconDropDownDataModel dataModel;
DropDownSelectionChoiceListener<Icon> choiceListener = t -> iconChanged(t);
public IconChooserPanel() {
build();
}
public void setSelectedIcon(Icon icon) {
if (icon == null) {
return;
}
if (!(icon instanceof UrlImageIcon)) {
icon = new ProtectedIcon(icon);
}
updateDropDownDataModel(icon);
updatePreviewLabel(icon);
// iconTextField.addActionListener(listener);
}
private void updateDropDownDataModel(Icon icon) {
Set<Icon> icons = ResourceManager.getLoadedUrlIcons();
icons.add(icon);
dataModel.setData(new ArrayList<>(icons));
dropDown.setSelectedValue(icon);
}
private void build() {
setLayout(new BorderLayout());
add(buildTopPanel(), BorderLayout.NORTH);
add(buildPreviewLabel(), BorderLayout.CENTER);
}
private Component buildTopPanel() {
JPanel panel = new JPanel(new BorderLayout());
dataModel = new IconDropDownDataModel();
dropDown = new DropDownSelectionTextField<>(dataModel) {
protected List<Icon> getMatchingData(String searchText) {
if (searchText.isBlank()) {
return ((IconDropDownDataModel) dataModel).getData();
}
return super.getMatchingData(searchText);
}
};
// dropDown.setConsumeEnterKeyPress(false);
// dropDown.addActionListener(e -> iconChanged());
dropDown.addDropDownSelectionChoiceListener(choiceListener);
// dropDown.addCellEditorListener(new CellEditorListener() {
//
// @Override
// public void editingStopped(ChangeEvent e) {
// Msg.debug(this, "Stopped");
// }
//
// @Override
// public void editingCanceled(ChangeEvent e) {
// Msg.debug(this, "Cancelled");
//
// }
// });
panel.add(dropDown, BorderLayout.CENTER);
// JButton browseButton = ButtonPanelFactory.createButton(ButtonPanelFactory.BROWSE_TYPE);
// panel.add(browseButton, BorderLayout.EAST);
// browseButton.addActionListener(e -> browse());
// iconTextField.addActionListener(listener);
return panel;
}
// private void iconChanged() {
// Icon icon = dropDown.getSelectedValue();
// Msg.debug(this, "action listener: icon changed " + icon);
// dropDown.getSelectedValue();
// }
private void iconChanged(Icon icon) {
boolean isDropDownWindowShowing = dropDown.isMatchingListShowing();
if (!isDropDownWindowShowing) {
updatePreviewLabel(icon);
doSetValue(icon);
}
}
private void browse() {
//TODO
}
private Component buildPreviewLabel() {
JPanel panel = new JPanel(new BorderLayout());
previewLabel = new GDLabel("");
previewLabel.setIcon(ResourceManager.getDefaultIcon());
// previewLabel.setPreferredSize(new Dimension(350, 50));
previewLabel.setHorizontalAlignment(SwingConstants.CENTER);
previewLabel.setVerticalAlignment(SwingConstants.CENTER);
// previewLabel.setMinimumSize(new Dimension(300, 50));
panel.add(previewLabel, BorderLayout.CENTER);
panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
return panel;
}
private void updatePreviewLabel(Icon icon) {
previewLabel.setIcon(icon);
int height = icon.getIconHeight();
int width = icon.getIconWidth();
Dimension d = previewLabel.getSize();
height = Math.max(d.height, height);
width = Math.max(d.width, width);
previewLabel.setPreferredSize(new Dimension(width, height));
previewLabel.invalidate();
iconChooserPanel.validate();
}
}
class IconDropDownDataModel extends DefaultDropDownSelectionDataModel<Icon> {
IconListCellRender renderer = new IconListCellRender();
public IconDropDownDataModel() {
super(Collections.emptyList(), IconPropertyEditor.this::iconToString);
}
List<Icon> getData() {
return data;
}
void setData(List<Icon> icons) {
Collections.sort(icons, comparator);
data = icons;
}
@Override
public List<Icon> getMatchingData(String searchText) {
if (searchText.isBlank()) {
return data;
}
searchText = searchText.toLowerCase();
List<Icon> results = new ArrayList<>();
for (Icon icon : data) {
String name = iconToString(icon);
if (name.toLowerCase().contains(searchText)) {
results.add(icon);
}
}
return results;
}
@Override
public ListCellRenderer<Icon> getListRenderer() {
return renderer;
}
}
class IconListCellRender extends GListCellRenderer<Icon> {
@Override
protected String getItemText(Icon icon) {
return iconToString(icon);
}
@Override
public Component getListCellRendererComponent(JList<? extends Icon> list, Icon icon,
int index, boolean isSelected, boolean hasFocus) {
JLabel label = (JLabel) super.getListCellRendererComponent(list, icon, index,
isSelected, hasFocus);
if (icon.getIconWidth() != 16 || icon.getIconHeight() != 16) {
icon = new ScaledImageIcon(icon, 16, 16);
}
label.setIcon(icon);
return label;
}
}
}

View File

@ -21,7 +21,10 @@ import java.io.*;
import java.util.Collections;
import java.util.List;
import javax.swing.Icon;
import ghidra.util.WebColors;
import resources.icons.UrlImageIcon;
public class FileGTheme extends GTheme {
public static final String FILE_PREFIX = "File:";
@ -117,7 +120,15 @@ public class FileGTheme extends GTheme {
if (iconValue.getReferenceId() != null) {
return iconValue.toExternalId(iconValue.getReferenceId());
}
return iconValue.getRawValue();
Icon icon = iconValue.getRawValue();
return iconToString(icon);
}
public static String iconToString(Icon icon) {
if (icon instanceof UrlImageIcon urlIcon) {
return urlIcon.getOriginalPath();
}
return "<UNKNOWN>";
}
private String getValueOutput(FontValue fontValue) {
@ -125,10 +136,14 @@ public class FileGTheme extends GTheme {
return fontValue.toExternalId(fontValue.getReferenceId());
}
Font font = fontValue.getRawValue();
return fontToString(font);
}
public static String fontToString(Font font) {
return String.format("%s-%s-%s", font.getName(), getStyleString(font), font.getSize());
}
private String getStyleString(Font font) {
private static String getStyleString(Font font) {
boolean bold = font.isBold();
boolean italic = font.isItalic();
if (bold && italic) {

View File

@ -1,323 +0,0 @@
/* ###
* 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.theme;
import java.awt.Font;
import java.awt.font.*;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.text.AttributedCharacterIterator.Attribute;
import java.text.CharacterIterator;
import java.util.*;
public class GFont extends Font implements Refreshable {
private String id;
private Font delegate;
public GFont(String id) {
super("Courier", Font.PLAIN, 12);
this.id = id;
delegate = Gui.getRawFont(id);
if (delegate == null) {
delegate = new Font("Courier", Font.PLAIN, 12);
}
}
public boolean isEquivalent(Font font) {
return delegate.equals(font);
}
public String getId() {
return id;
}
@Override
public AffineTransform getTransform() {
return delegate.getTransform();
}
@Override
public void refresh() {
Font font = Gui.getRawFont(id);
if (font != null) {
delegate = font;
}
}
@Override
public String getFamily() {
return delegate.getFamily();
}
@Override
public String getFamily(Locale l) {
return delegate.getFamily(l);
}
@Override
public String getPSName() {
return delegate.getPSName();
}
@Override
public String getName() {
return delegate.getName();
}
@Override
public String getFontName() {
return delegate.getFontName();
}
@Override
public String getFontName(Locale l) {
return delegate.getFontName(l);
}
@Override
public int getStyle() {
return delegate.getStyle();
}
@Override
public int getSize() {
return delegate.getSize();
}
@Override
public float getSize2D() {
return delegate.getSize2D();
}
@Override
public boolean isPlain() {
return delegate.isPlain();
}
@Override
public boolean isBold() {
return delegate.isBold();
}
@Override
public boolean isItalic() {
return delegate.isItalic();
}
@Override
public boolean isTransformed() {
return delegate.isTransformed();
}
@Override
public boolean hasLayoutAttributes() {
return delegate.hasLayoutAttributes();
}
@Override
public int hashCode() {
return id.hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!super.equals(obj)) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
GFont other = (GFont) obj;
return Objects.equals(id, other.id);
}
@Override
public String toString() {
return delegate.toString();
}
@Override
public int getNumGlyphs() {
return delegate.getNumGlyphs();
}
@Override
public int getMissingGlyphCode() {
return delegate.getMissingGlyphCode();
}
@Override
public byte getBaselineFor(char c) {
return delegate.getBaselineFor(c);
}
@Override
public Map<TextAttribute, ?> getAttributes() {
return delegate.getAttributes();
}
@Override
public Attribute[] getAvailableAttributes() {
return delegate.getAvailableAttributes();
}
@Override
public Font deriveFont(int newStyle, float newSize) {
return delegate.deriveFont(newStyle, newSize);
}
@Override
public Font deriveFont(int newStyle, AffineTransform trans) {
return delegate.deriveFont(newStyle, trans);
}
@Override
public Font deriveFont(float newSize) {
return delegate.deriveFont(newSize);
}
@Override
public Font deriveFont(AffineTransform trans) {
return delegate.deriveFont(trans);
}
@Override
public Font deriveFont(int newStyle) {
return delegate.deriveFont(newStyle);
}
@Override
public Font deriveFont(Map<? extends Attribute, ?> attributes) {
return delegate.deriveFont(attributes);
}
@Override
public boolean canDisplay(char c) {
return delegate.canDisplay(c);
}
@Override
public boolean canDisplay(int codePoint) {
return delegate.canDisplay(codePoint);
}
@Override
public int canDisplayUpTo(String str) {
return delegate.canDisplayUpTo(str);
}
@Override
public int canDisplayUpTo(char[] text, int start, int limit) {
return delegate.canDisplayUpTo(text, start, limit);
}
@Override
public int canDisplayUpTo(CharacterIterator iter, int start, int limit) {
return delegate.canDisplayUpTo(iter, start, limit);
}
@Override
public float getItalicAngle() {
return delegate.getItalicAngle();
}
@Override
public boolean hasUniformLineMetrics() {
return delegate.hasUniformLineMetrics();
}
@Override
public LineMetrics getLineMetrics(String str, FontRenderContext frc) {
return delegate.getLineMetrics(str, frc);
}
@Override
public LineMetrics getLineMetrics(String str, int beginIndex, int limit,
FontRenderContext frc) {
return delegate.getLineMetrics(str, beginIndex, limit, frc);
}
@Override
public LineMetrics getLineMetrics(char[] chars, int beginIndex, int limit,
FontRenderContext frc) {
return delegate.getLineMetrics(chars, beginIndex, limit, frc);
}
@Override
public LineMetrics getLineMetrics(CharacterIterator ci, int beginIndex, int limit,
FontRenderContext frc) {
return delegate.getLineMetrics(ci, beginIndex, limit, frc);
}
@Override
public Rectangle2D getStringBounds(String str, FontRenderContext frc) {
return delegate.getStringBounds(str, frc);
}
@Override
public Rectangle2D getStringBounds(String str, int beginIndex, int limit,
FontRenderContext frc) {
return delegate.getStringBounds(str, beginIndex, limit, frc);
}
@Override
public Rectangle2D getStringBounds(char[] chars, int beginIndex, int limit,
FontRenderContext frc) {
return delegate.getStringBounds(chars, beginIndex, limit, frc);
}
@Override
public Rectangle2D getStringBounds(CharacterIterator ci, int beginIndex, int limit,
FontRenderContext frc) {
return delegate.getStringBounds(ci, beginIndex, limit, frc);
}
@Override
public Rectangle2D getMaxCharBounds(FontRenderContext frc) {
return delegate.getMaxCharBounds(frc);
}
@Override
public GlyphVector createGlyphVector(FontRenderContext frc, String str) {
return delegate.createGlyphVector(frc, str);
}
@Override
public GlyphVector createGlyphVector(FontRenderContext frc, char[] chars) {
return delegate.createGlyphVector(frc, chars);
}
@Override
public GlyphVector createGlyphVector(FontRenderContext frc, CharacterIterator ci) {
return delegate.createGlyphVector(frc, ci);
}
@Override
public GlyphVector createGlyphVector(FontRenderContext frc, int[] glyphCodes) {
return delegate.createGlyphVector(frc, glyphCodes);
}
@Override
public GlyphVector layoutGlyphVector(FontRenderContext frc, char[] text, int start, int limit,
int flags) {
return delegate.layoutGlyphVector(frc, text, start, limit, flags);
}
}

View File

@ -20,21 +20,31 @@ import java.awt.Graphics;
import javax.swing.Icon;
import resources.ResourceManager;
import docking.theme.Refreshable;
import ghidra.util.datastruct.WeakStore;
public class GIcon implements Icon, Refreshable {
private static WeakStore<GIcon> inUseIcons = new WeakStore<>();
private String id;
private Icon delegate;
public GIcon(String id) {
this.id = id;
delegate = Gui.getRawIcon(id);
if (delegate == null) {
delegate = ResourceManager.getDefaultIcon();
public static void refreshAll() {
for (GIcon gIcon : inUseIcons.getValues()) {
gIcon.refresh();
}
}
public GIcon(String id) {
this(id, true);
}
public GIcon(String id, boolean validate) {
this.id = id;
delegate = Gui.getRawIcon(id, validate);
inUseIcons.add(this);
}
public String getId() {
return id;
}
@ -56,7 +66,7 @@ public class GIcon implements Icon, Refreshable {
@Override
public void refresh() {
Icon icon = Gui.getRawIcon(id);
Icon icon = Gui.getRawIcon(id, false);
if (icon != null) {
delegate = icon;
}

View File

@ -0,0 +1,26 @@
/* ###
* 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.theme;
import javax.swing.plaf.UIResource;
public class GIconUIResource extends GIcon implements UIResource {
public GIconUIResource(String id) {
super(id);
}
}

View File

@ -21,6 +21,8 @@ import java.io.File;
import java.io.IOException;
import java.util.Objects;
import javax.swing.Icon;
/**
* Class to store all the configurable appearance properties (Colors, Fonts, Icons, Look and Feel)
* in an application.
@ -124,10 +126,10 @@ public class GTheme extends GThemeValueMap {
/**
* Sets the icon for the given id
* @param id the id to associate with the given IconPath
* @param iconPath the path of the icon to assign to the given id
* @param icon the icon to assign to the given id
*/
public void setIcon(String id, String iconPath) {
addIconPath(new IconValue(id, null, iconPath));
public void setIcon(String id, Icon icon) {
addIcon(new IconValue(id, icon));
}
/**
@ -136,7 +138,7 @@ public class GTheme extends GThemeValueMap {
* @param refId the id of an indirect Icon lookup for the given id.
*/
public void setIconRef(String id, String refId) {
addIconPath(new IconValue(id, refId, null));
addIcon(new IconValue(id, refId));
}
@Override

View File

@ -41,7 +41,7 @@ public class GThemeValueMap {
}
}
public void addIconPath(IconValue value) {
public void addIcon(IconValue value) {
if (value != null) {
iconMap.put(value.getId(), value);
}
@ -62,7 +62,7 @@ public class GThemeValueMap {
public void load(GThemeValueMap valueMap) {
valueMap.colorMap.values().forEach(v -> addColor(v));
valueMap.fontMap.values().forEach(v -> addFont(v));
valueMap.iconMap.values().forEach(v -> addIconPath(v));
valueMap.iconMap.values().forEach(v -> addIcon(v));
}
@ -83,11 +83,11 @@ public class GThemeValueMap {
}
public boolean containsFont(String id) {
return colorMap.containsKey(id);
return fontMap.containsKey(id);
}
public boolean containsIconPath(String id) {
return colorMap.containsKey(id);
public boolean containsIcon(String id) {
return iconMap.containsKey(id);
}
public Object size() {
@ -122,9 +122,14 @@ public class GThemeValueMap {
}
for (IconValue icon : iconMap.values()) {
if (!icon.equals(base.getIcon(icon.getId()))) {
map.addIconPath(icon);
map.addIcon(icon);
}
}
return map;
}
public void removeFont(String id) {
fontMap.remove(id);
}
}

View File

@ -37,7 +37,7 @@ import utilities.util.reflection.ReflectionUtilities;
// TODO doc what this concept is
public class Gui {
public static final String THEME_DIR = "themes";
public static final String BACKGROUND_KEY = "color.bg.text";
private static final String THEME_PREFFERENCE_KEY = "Theme";
@ -53,8 +53,7 @@ public class Gui {
private static ThemePropertiesLoader themePropertiesLoader = new ThemePropertiesLoader();
private static Map<String, GColorUIResource> gColorMap = new HashMap<>();
private static JPanel jPanel;
private static Map<String, GIconUIResource> gIconMap = new HashMap<>();
static void setPropertiesLoader(ThemePropertiesLoader loader) {
themePropertiesLoader = loader;
@ -178,12 +177,15 @@ public class Gui {
return color.brighter();
}
public static GFont getFont(String id) {
return new GFont(id);
}
public static Font getFont(String id) {
FontValue font = currentValues.getFont(id);
if (font == null) {
Throwable t = getFilteredTrace();
public static GIcon getIcon(String id) {
return new GIcon(id);
Msg.error(Gui.class, "No font value registered for: " + id, t);
return null;
}
return font.get(currentValues);
}
public static void saveThemeToPreferences(GTheme theme) {
@ -227,27 +229,16 @@ public class Gui {
return color.get(currentValues);
}
static Font getRawFont(String id) {
FontValue font = currentValues.getFont(id);
if (font == null) {
Throwable t = getFilteredTrace();
Msg.error(Gui.class, "No font value registered for: " + id, t);
return null;
}
return font.get(currentValues);
}
public static Icon getRawIcon(String id) {
public static Icon getRawIcon(String id, boolean validate) {
IconValue icon = currentValues.getIcon(id);
if (icon == null) {
Throwable t = getFilteredTrace();
Msg.error(Gui.class, "No color value registered for: " + id, t);
return null;
if (validate) {
Throwable t = getFilteredTrace();
Msg.error(Gui.class, "No color value registered for: " + id, t);
}
return ResourceManager.getDefaultIcon();
}
String iconPath = icon.get(currentValues);
return ResourceManager.loadImage(iconPath);
return icon.get(currentValues);
}
private static Throwable getFilteredTrace() {
@ -270,6 +261,7 @@ public class Gui {
map.load(activeTheme);
currentValues = map;
GColor.refreshAll();
GIcon.refreshAll();
repaintAll();
}
@ -296,10 +288,14 @@ public class Gui {
private static Collection<GTheme> loadThemesFromFiles() {
List<File> fileList = new ArrayList<>();
FileFilter themeFileFilter = file -> file.getName().endsWith(GTheme.FILE_EXTENSION);
File dir = Application.getUserSettingsDirectory();
FileFilter themeFileFilter = file -> file.getName().endsWith(GTheme.FILE_EXTENSION);
fileList.addAll(Arrays.asList(dir.listFiles(themeFileFilter)));
File themeDir = new File(dir, THEME_DIR);
File[] files = themeDir.listFiles(themeFileFilter);
if (files != null) {
fileList.addAll(Arrays.asList(files));
}
List<GTheme> list = new ArrayList<>();
for (File file : fileList) {
@ -351,16 +347,55 @@ public class Gui {
return new DefaultTheme();
}
public static void setFont(FontValue newValue) {
currentValues.addFont(newValue);
// all fonts are direct (there is no GFont), so to we need to update the
// UiDefaults for java fonts. Ghidra fonts are expected to be "on the fly" (they
// call Gui.getFont(id) for every use.
String id = newValue.getId();
if (javaDefaults.containsFont(id)) {
UIManager.getDefaults().put(id, newValue.get(currentValues));
updateUIs();
}
else {
repaintAll();
}
}
public static void setColor(String id, Color color) {
setColor(new ColorValue(id, color));
}
public static void setColor(ColorValue colorValue) {
currentValues.addColor(colorValue);
// all colors use indirection via GColor, so to update all we need to do is refresh GColors
// and repaint
GColor.refreshAll();
repaintAll();
}
public static void setIcon(String id, Icon icon) {
setIcon(new IconValue(id, icon));
}
public static void setIcon(IconValue newValue) {
currentValues.addIcon(newValue);
// Icons are a mixed bag. Java Icons are direct and Ghidra Icons are indirect (to support static use)
// Mainly because Nimbus is buggy and can't handle non-nimbus Icons, so we can't wrap them
// So need to update UiDefaults for java icons. For Ghidra Icons, it is sufficient to refrech
// GIcons and repaint
String id = newValue.getId();
if (javaDefaults.containsIcon(id)) {
UIManager.getDefaults().put(id, newValue.get(currentValues));
updateUIs();
}
else {
GIcon.refreshAll();
repaintAll();
}
}
private static void repaintAll() {
for (Window window : Window.getWindows()) {
window.repaint();
@ -368,6 +403,7 @@ public class Gui {
}
public static GColorUIResource getGColorUiResource(String id) {
GColorUIResource gColor = gColorMap.get(id);
if (gColor == null) {
gColor = new GColorUIResource(id);
@ -376,6 +412,16 @@ public class Gui {
return gColor;
}
public static GIconUIResource getGIconUiResource(String id) {
GIconUIResource gIcon = gIconMap.get(id);
if (gIcon == null) {
gIcon = new GIconUIResource(id);
gIconMap.put(id, gIcon);
}
return gIcon;
}
public static void setJavaDefaults(GThemeValueMap map) {
javaDefaults = map;
buildCurrentValues();
@ -417,4 +463,5 @@ public class Gui {
}
return currentDefaults;
}
}

View File

@ -15,17 +15,24 @@
*/
package docking.theme;
import ghidra.util.Msg;
import javax.swing.Icon;
public class IconValue extends ThemeValue<String> {
import ghidra.util.Msg;
import resources.ResourceManager;
public class IconValue extends ThemeValue<Icon> {
static final String ICON_ID_PREFIX = "icon.";
public static final String LAST_RESORT_DEFAULT = "images/bomb.gif";
private static final String EXTERNAL_PREFIX = "[icon]";
public IconValue(String id, String refId, String iconPath) {
super(id, refId, iconPath);
public IconValue(String id, Icon icon) {
super(id, null, icon);
}
public IconValue(String id, String refId) {
super(id, refId, null);
}
@Override
@ -34,10 +41,10 @@ public class IconValue extends ThemeValue<String> {
}
@Override
protected String getUnresolvedReferenceValue(String id) {
protected Icon getUnresolvedReferenceValue(String id) {
Msg.warn(this,
"Could not resolve indirect icon path for" + id + ", using last resort default");
return LAST_RESORT_DEFAULT;
return ResourceManager.getDefaultIcon();
}
@Override
@ -66,7 +73,7 @@ public class IconValue extends ThemeValue<String> {
}
@Override
protected int compareValues(String v1, String v2) {
return v1.compareTo(v2);
protected int compareValues(Icon v1, Icon v2) {
return v1.toString().compareTo(v2.toString());
}
}

View File

@ -20,11 +20,15 @@ import java.awt.Font;
import java.io.*;
import java.util.*;
import javax.swing.Icon;
import javax.swing.plaf.FontUIResource;
import org.apache.commons.collections4.map.HashedMap;
import generic.jar.ResourceFile;
import ghidra.util.Msg;
import ghidra.util.WebColors;
import resources.ResourceManager;
public class ThemePropertyFileReader {
@ -112,7 +116,7 @@ public class ThemePropertyFileReader {
valueMap.addFont(parseFontProperty(key, value, lineNumber));
}
else if (IconValue.isIconKey(key)) {
valueMap.addIconPath(parseIconProperty(key, value));
valueMap.addIcon(parseIconProperty(key, value));
}
else {
error(lineNumber, "Can't process property: " + key + " = " + value);
@ -122,9 +126,10 @@ public class ThemePropertyFileReader {
private IconValue parseIconProperty(String key, String value) {
if (IconValue.isIconKey(value)) {
return new IconValue(key, value, null);
return new IconValue(key, value);
}
return new IconValue(key, null, value);
Icon icon = ResourceManager.loadImage(value);
return new IconValue(key, icon);
}
private FontValue parseFontProperty(String key, String value, int lineNumber) {
@ -135,7 +140,7 @@ public class ThemePropertyFileReader {
if (font == null) {
error(lineNumber, "Could not parse Color: " + value);
}
return font == null ? null : new FontValue(key, font);
return font == null ? null : new FontValue(key, new FontUIResource(font));
}
private ColorValue parseColorProperty(String key, String value, int lineNumber) {

View File

@ -50,6 +50,7 @@ public class ThemeReader extends ThemePropertyFileReader {
// processValues expects only colors, fonts, and icons
themeSection.remove(GTheme.THEME_NAME_KEY);
themeSection.remove(GTheme.THEME_LOOK_AND_FEEL_KEY);
themeSection.remove(GTheme.THEME_USE_DARK_DEFAULTS);
processValues(theme, themeSection);
}

View File

@ -0,0 +1,47 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package docking.theme.gui;
import java.awt.Color;
import java.beans.PropertyChangeListener;
import docking.options.editor.ColorPropertyEditor;
import docking.theme.*;
/**
* Editor for Theme colors
*/
public class ColorValueEditor extends ThemeValueEditor<Color> {
/**
* Constructor
* @param listener the {@link PropertyChangeListener} to be notified when changes are made
*/
public ColorValueEditor(PropertyChangeListener listener) {
super("Color", listener, new ColorPropertyEditor());
}
@Override
protected Color getRawValue(String id) {
return Gui.getRawColor(id);
}
@Override
protected ThemeValue<Color> createNewThemeValue(String id, Color color) {
return new ColorValue(id, color);
}
}

View File

@ -0,0 +1,56 @@
/* ###
* 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.theme.gui;
import java.awt.Font;
import java.beans.PropertyChangeListener;
import javax.swing.plaf.FontUIResource;
import javax.swing.plaf.UIResource;
import docking.options.editor.FontPropertyEditor;
import docking.theme.*;
/**
* Editor for Theme fonts
*/
public class FontValueEditor extends ThemeValueEditor<Font> {
/**
* Constructor
* @param listener the {@link PropertyChangeListener} to be notified when changes are made
*/
public FontValueEditor(PropertyChangeListener listener) {
super("Font", listener, new FontPropertyEditor());
}
@Override
protected Font getRawValue(String id) {
return Gui.getFont(id);
}
@Override
protected ThemeValue<Font> createNewThemeValue(String id, Font font) {
// We need user changed values to be UIResources, otherwise the next time they change
// the value, it won't be honored because setting UIDefaults only affects components
// that have current UIResource fonts (So that programatic settings won't be overridden)
if (!(font instanceof UIResource)) {
font = new FontUIResource(font);
}
return new FontValue(id, font);
}
}

View File

@ -0,0 +1,48 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package docking.theme.gui;
import java.beans.PropertyChangeListener;
import javax.swing.Icon;
import docking.options.editor.IconPropertyEditor;
import docking.theme.*;
/**
* Editor for Theme fonts
*/
public class IconValueEditor extends ThemeValueEditor<Icon> {
/**
* Constructor
* @param listener the {@link PropertyChangeListener} to be notified when changes are made
*/
public IconValueEditor(PropertyChangeListener listener) {
super("Icon", listener, new IconPropertyEditor());
}
@Override
protected Icon getRawValue(String id) {
return Gui.getRawIcon(id, true);
}
@Override
protected ThemeValue<Icon> createNewThemeValue(String id, Icon icon) {
return new IconValue(id, icon);
}
}

View File

@ -0,0 +1,57 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package docking.theme.gui;
import java.awt.Component;
import java.awt.Graphics;
import javax.swing.Icon;
import resources.ResourceManager;
public class ProtectedIcon implements Icon {
Icon bomb = ResourceManager.getDefaultIcon();
Icon delegate;
boolean isError = false;
public ProtectedIcon(Icon delegate) {
this.delegate = delegate;
}
public boolean hasError() {
return isError;
}
@Override
public void paintIcon(Component c, Graphics g, int x, int y) {
try {
delegate.paintIcon(c, g, x, y);
}
catch (Exception e) {
bomb.paintIcon(c, g, x, y);
}
}
@Override
public int getIconWidth() {
return delegate.getIconWidth();
}
@Override
public int getIconHeight() {
return delegate.getIconHeight();
}
}

View File

@ -1,100 +0,0 @@
/* ###
* 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.theme.gui;
import java.awt.BorderLayout;
import java.awt.Color;
import javax.swing.*;
import javax.swing.event.ChangeListener;
import docking.DialogComponentProvider;
import docking.DockingWindowManager;
import docking.options.editor.GhidraColorChooser;
import docking.theme.ColorValue;
import docking.theme.Gui;
public class ThemeColorEditorDialog extends DialogComponentProvider {
private ColorValue startingColorValue;
private ColorValue currentColorValue;
private ThemeDialog themeDialog;
private GhidraColorChooser colorChooser;
private ChangeListener colorChangeListener = e -> colorChanged();
public ThemeColorEditorDialog(ThemeDialog themeDialog) {
super("Theme Color Editor", false);
this.themeDialog = themeDialog;
addWorkPanel(buildColorPanel());
addOKButton();
addCancelButton();
}
public void editColor(ColorValue colorValue) {
this.startingColorValue = colorValue;
this.currentColorValue = colorValue;
setTitle("Edit Color For: " + colorValue.getId());
Color color = Gui.getRawColor(startingColorValue.getId());
colorChooser.getSelectionModel().removeChangeListener(colorChangeListener);
colorChooser.setColor(color);
colorChooser.getSelectionModel().addChangeListener(colorChangeListener);
if (!isShowing()) {
DockingWindowManager.showDialog(themeDialog.getComponent(), this);
}
}
private JComponent buildColorPanel() {
JPanel panel = new JPanel(new BorderLayout());
panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
colorChooser = new GhidraColorChooser();
panel.add(colorChooser);
return panel;
}
@Override
protected void okCallback() {
currentColorValue = null;
startingColorValue = null;
close();
themeDialog.colorEditorClosed();
}
@Override
protected void cancelCallback() {
restoreOriginalColor();
currentColorValue = null;
startingColorValue = null;
close();
themeDialog.colorEditorClosed();
}
private void restoreOriginalColor() {
themeDialog.colorChanged(currentColorValue, startingColorValue);
currentColorValue = startingColorValue;
}
private void colorChanged() {
Color newColor = colorChooser.getColor();
ColorValue newColorValue = new ColorValue(startingColorValue.getId(), newColor);
themeDialog.colorChanged(currentColorValue, newColorValue);
currentColorValue = newColorValue;
}
}

View File

@ -69,7 +69,7 @@ public class ThemeColorTableModel extends GDynamicColumnTableModel<ColorValue, O
@Override
public String getName() {
return "Users";
return "Colors";
}
@Override
@ -181,7 +181,7 @@ public class ThemeColorTableModel extends GDynamicColumnTableModel<ColorValue, O
ResolvedColor resolved = (ResolvedColor) data.getValue();
String text = getValueText(resolved);
Color color = resolved == null ? GThemeDefaults.Colors.BACKGROUND : resolved.color;
Color color = resolved == null ? GThemeDefaults.Colors.BACKGROUND : resolved.color();
label.setText(text);
label.setIcon(new SwatchIcon(color, label.getForeground()));
label.setOpaque(true);
@ -192,10 +192,10 @@ public class ThemeColorTableModel extends GDynamicColumnTableModel<ColorValue, O
if (resolvedColor == null) {
return "<No Value>";
}
if (resolvedColor.refId != null) {
return resolvedColor.refId;
if (resolvedColor.refId() != null) {
return resolvedColor.refId();
}
Color color = resolvedColor.color;
Color color = resolvedColor.color();
String text = WebColors.toString(color, false);
String name = WebColors.toWebColorName(color);
if (name != null) {
@ -239,15 +239,5 @@ public class ThemeColorTableModel extends GDynamicColumnTableModel<ColorValue, O
}
}
class ResolvedColor {
String id;
String refId;
Color color;
ResolvedColor(String id, String refId, Color color) {
this.id = id;
this.refId = refId;
this.color = color;
}
}
record ResolvedColor(String id, String refId, Color color) {/**/}
}

View File

@ -18,6 +18,7 @@ package docking.theme.gui;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.event.*;
import java.beans.PropertyChangeEvent;
import java.io.File;
import java.io.IOException;
import java.util.*;
@ -41,12 +42,16 @@ import docking.widgets.table.GTable;
import ghidra.framework.Application;
import ghidra.util.*;
import ghidra.util.filechooser.ExtensionFileFilter;
import resources.Icons;
public class ThemeDialog extends DialogComponentProvider {
private static ThemeDialog INSTANCE;
private ThemeColorTableModel colorTableModel;
private ThemeColorEditorDialog dialog;
private ThemeFontTableModel fontTableModel;
private ThemeIconTableModel iconTableModel;
private ColorValueEditor colorEditor = new ColorValueEditor(this::colorValueChanged);
private FontValueEditor fontEditor = new FontValueEditor(this::fontValueChanged);
private IconValueEditor iconEditor = new IconValueEditor(this::iconValueChanged);
// stores the original value for ids whose value has changed
private GThemeValueMap changedValuesMap = new GThemeValueMap();
@ -70,22 +75,22 @@ public class ThemeDialog extends DialogComponentProvider {
}
private void createActions() {
DockingAction importAction = new ActionBuilder("Import Theme", getTitle())
.toolBarIcon(Icons.NAVIGATE_ON_INCOMING_EVENT_ICON)
.onAction(e -> importTheme())
.build();
DockingAction importAction =
new ActionBuilder("Import Theme", getTitle()).toolBarIcon(new GIcon("icon.navigate.in"))
.onAction(e -> importTheme())
.build();
addAction(importAction);
DockingAction exportAction = new ActionBuilder("Export Theme", getTitle())
.toolBarIcon(Icons.NAVIGATE_ON_OUTGOING_EVENT_ICON)
.toolBarIcon(new GIcon("icon.navigate.out"))
.onAction(e -> exportTheme())
.build();
addAction(exportAction);
DockingAction reloadDefaultsAction =
new ActionBuilder("Reload Ghidra Defaults", getTitle()).toolBarIcon(Icons.REFRESH_ICON)
.onAction(e -> reloadDefaultsCallback())
.build();
DockingAction reloadDefaultsAction = new ActionBuilder("Reload Ghidra Defaults", getTitle())
.toolBarIcon(new GIcon("icon.refresh"))
.onAction(e -> reloadDefaultsCallback())
.build();
addAction(reloadDefaultsAction);
}
@ -144,6 +149,8 @@ public class ThemeDialog extends DialogComponentProvider {
private void reset() {
changedValuesMap.clear();
colorTableModel.reloadAll();
fontTableModel.reloadAll();
iconTableModel.reloadAll();
updateButtons();
updateCombo();
}
@ -211,8 +218,12 @@ public class ThemeDialog extends DialogComponentProvider {
private File getSaveFile(String themeName) {
File dir = Application.getUserSettingsDirectory();
File themeDir = new File(dir, Gui.THEME_DIR);
if (!themeDir.exists()) {
themeDir.mkdir();
}
String cleanedName = themeName.replaceAll(" ", "_") + GTheme.FILE_EXTENSION;
return new File(dir, cleanedName);
return new File(themeDir, cleanedName);
}
private void importTheme() {
@ -252,15 +263,18 @@ public class ThemeDialog extends DialogComponentProvider {
private void themeComboChanged(ItemEvent e) {
if (e.getStateChange() == ItemEvent.SELECTED) {
String themeName = (String) e.getItem();
if (hasChanges()) {
Msg.debug(this, "has changes");
}
Swing.runLater(() -> {
GTheme theme = Gui.getTheme(themeName);
Gui.setTheme(theme);
if (theme.getLookAndFeelType() == LafType.GTK) {
setStatusText(
"Warning - GTK does not support changing it's component colors. You can still change Ghidra colors.",
"Warning - Themes using the GTK LookAndFeel do not support changing java component colors, fonts or icons. You can still change Ghidra values.",
MessageType.ERROR, true);
}
else if (theme.getLookAndFeelType() == LafType.NIMBUS) {
setStatusText(
"Warning - Themes using the Nimbus LookAndFeel do not support changing java component fonts or icons. You can still change Ghidra values.",
MessageType.ERROR, true);
}
else {
@ -268,6 +282,8 @@ public class ThemeDialog extends DialogComponentProvider {
}
changedValuesMap.clear();
colorTableModel.reloadAll();
fontTableModel.reloadAll();
iconTableModel.reloadAll();
});
}
}
@ -277,14 +293,21 @@ public class ThemeDialog extends DialogComponentProvider {
}
protected void editColor(ColorValue value) {
if (dialog == null) {
dialog = new ThemeColorEditorDialog(this);
}
dialog.editColor(value);
colorEditor.editValue(value);
}
void colorChanged(ColorValue oldValue, ColorValue newValue) {
updateChanagedValueMap(oldValue, newValue);
protected void editFont(FontValue value) {
fontEditor.editValue(value);
}
protected void editIcon(IconValue value) {
iconEditor.editValue(value);
}
void colorValueChanged(PropertyChangeEvent event) {
ColorValue oldValue = (ColorValue) event.getOldValue();
ColorValue newValue = (ColorValue) event.getNewValue();
updateChangedValueMap(oldValue, newValue);
// run later - don't rock the boat in the middle of a listener callback
Swing.runLater(() -> {
Gui.setColor(newValue);
@ -292,7 +315,29 @@ public class ThemeDialog extends DialogComponentProvider {
});
}
private void updateChanagedValueMap(ColorValue oldValue, ColorValue newValue) {
void fontValueChanged(PropertyChangeEvent event) {
FontValue oldValue = (FontValue) event.getOldValue();
FontValue newValue = (FontValue) event.getNewValue();
updateChangedValueMap(oldValue, newValue);
// run later - don't rock the boat in the middle of a listener callback
Swing.runLater(() -> {
Gui.setFont(newValue);
fontTableModel.reloadCurrent();
});
}
void iconValueChanged(PropertyChangeEvent event) {
IconValue oldValue = (IconValue) event.getOldValue();
IconValue newValue = (IconValue) event.getNewValue();
updateChangedValueMap(oldValue, newValue);
// run later - don't rock the boat in the middle of a listener callback
Swing.runLater(() -> {
Gui.setIcon(newValue);
iconTableModel.reloadCurrent();
});
}
private void updateChangedValueMap(ColorValue oldValue, ColorValue newValue) {
ColorValue originalValue = changedValuesMap.getColor(oldValue.getId());
if (originalValue == null) {
changedValuesMap.addColor(oldValue);
@ -304,16 +349,36 @@ public class ThemeDialog extends DialogComponentProvider {
updateButtons();
}
private void updateChangedValueMap(FontValue oldValue, FontValue newValue) {
FontValue originalValue = changedValuesMap.getFont(oldValue.getId());
if (originalValue == null) {
changedValuesMap.addFont(oldValue);
}
else if (originalValue.equals(newValue)) {
// if restoring the original color, remove it from the map of changes
changedValuesMap.removeFont(oldValue.getId());
}
updateButtons();
}
private void updateChangedValueMap(IconValue oldValue, IconValue newValue) {
IconValue originalValue = changedValuesMap.getIcon(oldValue.getId());
if (originalValue == null) {
changedValuesMap.addIcon(oldValue);
}
else if (originalValue.equals(newValue)) {
// if restoring the original color, remove it from the map of changes
changedValuesMap.removeFont(oldValue.getId());
}
updateButtons();
}
private void updateButtons() {
boolean hasChanges = hasChanges();
saveButton.setEnabled(hasChanges);
restoreButton.setEnabled(hasChanges);
}
void colorEditorClosed() {
dialog = null;
}
private JComponent createMainPanel() {
JPanel panel = new JPanel();
@ -362,9 +427,89 @@ public class ThemeDialog extends DialogComponentProvider {
private Component buildTabedTables() {
JTabbedPane tabbedPane = new JTabbedPane();
tabbedPane.add("Colors", buildColorTable());
tabbedPane.add("Fonts", buildFontTable());
tabbedPane.add("Icons", buildIconTable());
return tabbedPane;
}
private JComponent buildFontTable() {
fontTableModel = new ThemeFontTableModel();
GFilterTable<FontValue> filterTable = new GFilterTable<>(fontTableModel);
GTable table = filterTable.getTable();
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
table.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
FontValue fontValue = filterTable.getSelectedRowObject();
if (fontValue != null) {
editFont(fontValue);
}
e.consume();
}
}
});
table.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) {
FontValue value = filterTable.getItemAt(e.getPoint());
int col = filterTable.getColumn(e.getPoint());
TableColumn column = table.getColumnModel().getColumn(col);
Object identifier = column.getIdentifier();
if ("Current Font".equals(identifier) || "Id".equals(identifier)) {
editFont(value);
}
}
}
});
return filterTable;
}
private JComponent buildIconTable() {
iconTableModel = new ThemeIconTableModel();
GFilterTable<IconValue> filterTable = new GFilterTable<>(iconTableModel);
GTable table = filterTable.getTable();
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
table.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
IconValue iconValue = filterTable.getSelectedRowObject();
if (iconValue != null) {
editIcon(iconValue);
}
e.consume();
}
}
});
table.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) {
IconValue value = filterTable.getItemAt(e.getPoint());
int col = filterTable.getColumn(e.getPoint());
TableColumn column = table.getColumnModel().getColumn(col);
Object identifier = column.getIdentifier();
if ("Current Icon".equals(identifier) || "Id".equals(identifier)) {
editIcon(value);
}
}
}
});
return filterTable;
}
private JComponent buildColorTable() {
colorTableModel = new ThemeColorTableModel();
@ -390,8 +535,6 @@ public class ThemeDialog extends DialogComponentProvider {
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) {
ColorValue value = filterTable.getItemAt(e.getPoint());
Object cellValue = filterTable.getCellValue(e.getPoint());
// editColor(value);
int col = filterTable.getColumn(e.getPoint());
TableColumn column = table.getColumnModel().getColumn(col);
@ -433,5 +576,4 @@ public class ThemeDialog extends DialogComponentProvider {
DockingWindowManager.showDialog(INSTANCE);
}
}

View File

@ -0,0 +1,204 @@
/* ###
* 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.theme.gui;
import java.awt.Component;
import java.awt.Font;
import java.util.Comparator;
import java.util.List;
import java.util.function.Supplier;
import javax.swing.JLabel;
import docking.theme.*;
import docking.widgets.table.*;
import ghidra.docking.settings.Settings;
import ghidra.framework.plugintool.ServiceProvider;
import ghidra.framework.plugintool.ServiceProviderStub;
import ghidra.util.table.column.AbstractGColumnRenderer;
import ghidra.util.table.column.GColumnRenderer;
public class ThemeFontTableModel extends GDynamicColumnTableModel<FontValue, Object> {
private List<FontValue> fonts;
private GThemeValueMap currentValues;
private GThemeValueMap themeValues;
private GThemeValueMap defaultValues;
public ThemeFontTableModel() {
super(new ServiceProviderStub());
load();
}
private void load() {
currentValues = Gui.getAllValues();
fonts = currentValues.getFonts();
themeValues = new GThemeValueMap(currentValues);
defaultValues = Gui.getDefaults();
}
@Override
public String getName() {
return "Fonts";
}
@Override
public List<FontValue> getModelData() {
return fonts;
}
@Override
protected TableColumnDescriptor<FontValue> createTableColumnDescriptor() {
TableColumnDescriptor<FontValue> descriptor = new TableColumnDescriptor<>();
descriptor.addVisibleColumn(new IdColumn());
descriptor.addVisibleColumn(new FontValueColumn("Current Font", () -> currentValues));
descriptor.addVisibleColumn(new FontValueColumn("Theme Font", () -> themeValues));
descriptor.addVisibleColumn(new FontValueColumn("Default Font", () -> defaultValues));
return descriptor;
}
@Override
public Object getDataSource() {
return null;
}
class IdColumn extends AbstractDynamicTableColumn<FontValue, String, Object> {
@Override
public String getColumnName() {
return "Id";
}
@Override
public String getValue(FontValue fontValue, Settings settings, Object data,
ServiceProvider provider) throws IllegalArgumentException {
return fontValue.getId();
}
@Override
public int getColumnPreferredWidth() {
return 300;
}
}
class FontValueColumn extends AbstractDynamicTableColumn<FontValue, ResolvedFont, Object> {
private ThemeFontRenderer renderer;
private String name;
private Supplier<GThemeValueMap> valueSupplier;
FontValueColumn(String name, Supplier<GThemeValueMap> supplier) {
this.name = name;
this.valueSupplier = supplier;
renderer = new ThemeFontRenderer();
}
@Override
public String getColumnName() {
return name;
}
@Override
public ResolvedFont getValue(FontValue fontValue, Settings settings, Object data,
ServiceProvider provider) throws IllegalArgumentException {
GThemeValueMap valueMap = valueSupplier.get();
String id = fontValue.getId();
FontValue value = valueMap.getFont(id);
if (value == null) {
return null;
}
Font font = value.get(valueMap);
return new ResolvedFont(id, value.getReferenceId(), font);
}
@Override
public GColumnRenderer<ResolvedFont> getColumnRenderer() {
return renderer;
}
public Comparator<ResolvedFont> getComparator() {
return (v1, v2) -> {
if (v1 == null && v2 == null) {
return 0;
}
if (v1 == null) {
return 1;
}
if (v2 == null) {
return -1;
}
return v1.font().toString().compareTo(v2.font().toString());
};
}
@Override
public int getColumnPreferredWidth() {
return 300;
}
}
private class ThemeFontRenderer extends AbstractGColumnRenderer<ResolvedFont> {
public ThemeFontRenderer() {
setFont(new Font("Monospaced", Font.PLAIN, 12));
}
@Override
public Component getTableCellRendererComponent(GTableCellRenderingData data) {
JLabel label = (JLabel) super.getTableCellRendererComponent(data);
ResolvedFont resolved = (ResolvedFont) data.getValue();
String text = getValueText(resolved);
label.setText(text);
label.setOpaque(true);
return label;
}
private String getValueText(ResolvedFont resolvedFont) {
if (resolvedFont == null) {
return "<No Value>";
}
Font font = resolvedFont.font();
String fontString = FileGTheme.fontToString(font);
if (resolvedFont.refId() != null) {
return resolvedFont.refId() + " [" + fontString + "]";
}
return fontString;
}
@Override
public String getFilterString(ResolvedFont fontValue, Settings settings) {
return getValueText(fontValue);
}
}
record ResolvedFont(String id, String refId, Font font) {/**/}
public void reloadCurrent() {
currentValues = Gui.getAllValues();
fonts = currentValues.getFonts();
fireTableDataChanged();
}
public void reloadAll() {
load();
fireTableDataChanged();
}
}

View File

@ -0,0 +1,229 @@
/* ###
* 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.theme.gui;
import java.awt.Component;
import java.awt.Font;
import java.util.Comparator;
import java.util.List;
import java.util.function.Supplier;
import javax.swing.*;
import docking.theme.*;
import docking.widgets.table.*;
import ghidra.docking.settings.Settings;
import ghidra.framework.plugintool.ServiceProvider;
import ghidra.framework.plugintool.ServiceProviderStub;
import ghidra.util.Msg;
import ghidra.util.table.column.AbstractGColumnRenderer;
import ghidra.util.table.column.GColumnRenderer;
import resources.icons.*;
public class ThemeIconTableModel extends GDynamicColumnTableModel<IconValue, Object> {
private List<IconValue> icons;
private GThemeValueMap currentValues;
private GThemeValueMap themeValues;
private GThemeValueMap defaultValues;
public ThemeIconTableModel() {
super(new ServiceProviderStub());
load();
}
private void load() {
Msg.debug(this, "loading");
currentValues = Gui.getAllValues();
icons = currentValues.getIcons();
themeValues = new GThemeValueMap(currentValues);
defaultValues = Gui.getDefaults();
}
@Override
public String getName() {
return "Fonts";
}
@Override
public List<IconValue> getModelData() {
return icons;
}
@Override
protected TableColumnDescriptor<IconValue> createTableColumnDescriptor() {
TableColumnDescriptor<IconValue> descriptor = new TableColumnDescriptor<>();
descriptor.addVisibleColumn(new IdColumn());
descriptor.addVisibleColumn(new IconValueColumn("Current Icon", () -> currentValues));
descriptor.addVisibleColumn(new IconValueColumn("Theme Icon", () -> themeValues));
descriptor.addVisibleColumn(new IconValueColumn("Default Icon", () -> defaultValues));
return descriptor;
}
@Override
public Object getDataSource() {
return null;
}
class IdColumn extends AbstractDynamicTableColumn<IconValue, String, Object> {
@Override
public String getColumnName() {
return "Id";
}
@Override
public String getValue(IconValue iconValue, Settings settings, Object data,
ServiceProvider provider) throws IllegalArgumentException {
return iconValue.getId();
}
@Override
public int getColumnPreferredWidth() {
return 300;
}
}
class IconValueColumn extends AbstractDynamicTableColumn<IconValue, ResolvedIcon, Object> {
private ThemeIconRenderer renderer;
private String name;
private Supplier<GThemeValueMap> valueSupplier;
IconValueColumn(String name, Supplier<GThemeValueMap> supplier) {
this.name = name;
this.valueSupplier = supplier;
renderer = new ThemeIconRenderer();
}
@Override
public String getColumnName() {
return name;
}
@Override
public ResolvedIcon getValue(IconValue iconValue, Settings settings, Object data,
ServiceProvider provider) throws IllegalArgumentException {
GThemeValueMap valueMap = valueSupplier.get();
String id = iconValue.getId();
IconValue value = valueMap.getIcon(id);
if (value == null) {
return null;
}
Icon icon = value.get(valueMap);
return new ResolvedIcon(id, value.getReferenceId(), icon);
}
@Override
public GColumnRenderer<ResolvedIcon> getColumnRenderer() {
return renderer;
}
public Comparator<ResolvedIcon> getComparator() {
return (v1, v2) -> {
if (v1 == null && v2 == null) {
return 0;
}
if (v1 == null) {
return 1;
}
if (v2 == null) {
return -1;
}
return v1.icon().toString().compareTo(v2.icon().toString());
};
}
@Override
public int getColumnPreferredWidth() {
return 300;
}
}
private class ThemeIconRenderer extends AbstractGColumnRenderer<ResolvedIcon> {
public ThemeIconRenderer() {
setFont(new Font("Monospaced", Font.PLAIN, 12));
}
@Override
public Component getTableCellRendererComponent(GTableCellRenderingData data) {
Component comp = super.getTableCellRendererComponent(data);
JLabel label = (JLabel) comp;
ResolvedIcon resolved = (ResolvedIcon) data.getValue();
String text = getValueText(resolved);
Icon icon = prepareIcon(resolved.icon());
label.setIcon(icon);
label.setText(text);
label.setOpaque(true);
return label;
}
private Icon prepareIcon(Icon icon) {
if (!(icon instanceof LazyImageIcon)) {
icon = new ProtectedIcon(icon);
}
if (icon.getIconWidth() != 16 && icon.getIconHeight() != 16) {
icon = new ScaledImageIcon(icon, 16, 16);
}
return icon;
}
private String getValueText(ResolvedIcon resolvedIcon) {
if (resolvedIcon == null) {
return "<No Value>";
}
Icon icon = resolvedIcon.icon();
String sizeString = "[" + icon.getIconWidth() + "x" + icon.getIconHeight() + "] ";
String iconString = "<Internal>";
if (icon instanceof UrlImageIcon urlIcon) {
iconString = urlIcon.getOriginalPath();
}
else if (icon instanceof ImageIcon imageIcon) {
String description = imageIcon.getDescription();
if (description != null) {
iconString = "[" + description + "]";
}
}
if (resolvedIcon.refId() != null) {
iconString = resolvedIcon.refId() + " [" + iconString + "]";
}
return sizeString + iconString;
}
@Override
public String getFilterString(ResolvedIcon iconValue, Settings settings) {
return getValueText(iconValue);
}
}
record ResolvedIcon(String id, String refId, Icon icon) {/**/}
public void reloadCurrent() {
currentValues = Gui.getAllValues();
icons = currentValues.getIcons();
fireTableDataChanged();
}
public void reloadAll() {
load();
fireTableDataChanged();
}
}

View File

@ -0,0 +1,142 @@
/* ###
* 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.theme.gui;
import java.awt.BorderLayout;
import java.beans.*;
import javax.swing.*;
import docking.DialogComponentProvider;
import docking.DockingWindowManager;
import docking.theme.ThemeValue;
/**
* Base class for Theme properties (Colors, Fonts, and Icons)
*
* @param <T> the base property (Color, Font, or Icon)
*/
public abstract class ThemeValueEditor<T> {
private PropertyChangeListener clientListener;
protected ThemeValue<T> currentThemeValue;
private EditorDialog dialog;
private String typeName;
private PropertyEditor editor;
/**
* Constructor
*
* @param typeName the name of the type (Used in the dialog title)
* @param listener the {@link PropertyChangeListener} to be notified for changes
* @param editor the standard property editor for the type
*/
protected ThemeValueEditor(String typeName, PropertyChangeListener listener,
PropertyEditor editor) {
this.typeName = typeName;
this.clientListener = listener;
this.editor = editor;
}
/**
* Edits the ThemeValue by invoking the appropriate dialog for editing the type
* @param themeValue the value to be edited
*/
public void editValue(ThemeValue<T> themeValue) {
this.currentThemeValue = themeValue;
T value = getRawValue(themeValue.getId());
if (dialog == null) {
dialog = new EditorDialog(value);
DockingWindowManager.showDialog(dialog);
}
else {
dialog.setValue(value);
dialog.toFront();
}
}
/**
* Returns the actual value (Color, Font, or Icon)
* @param id the theme property id for the value
* @return the current stored value for the id
*/
protected abstract T getRawValue(String id);
/**
* Factory method for creating the ThemeValue of the correct type.
* @param id the id for theme property
* @param newValue the new value for the underlying type (Color, Font, or Icon)
* @return the new ThemeValue for the type
*/
protected abstract ThemeValue<T> createNewThemeValue(String id, T newValue);
private void valueChanged(T newValue) {
ThemeValue<T> oldValue = currentThemeValue;
String id = oldValue.getId();
PropertyChangeEvent event =
new PropertyChangeEvent(this, id, oldValue, createNewThemeValue(id, newValue));
clientListener.propertyChange(event);
}
class EditorDialog extends DialogComponentProvider {
private PropertyChangeListener internalListener = ev -> editorChanged();
private T originalValue;
protected EditorDialog(T initialValue) {
super("Edit " + typeName + ": " + currentThemeValue.getId(), false, false, true, false);
this.originalValue = initialValue;
addWorkPanel(buildWorkPanel(initialValue));
addOKButton();
addCancelButton();
setRememberSize(false);
}
@SuppressWarnings("unchecked")
private void editorChanged() {
valueChanged((T) editor.getValue());
}
JComponent buildWorkPanel(T initialValue) {
JPanel panel = new JPanel(new BorderLayout());
panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 0, 10));
panel.add(editor.getCustomEditor(), BorderLayout.CENTER);
editor.setValue(initialValue);
editor.addPropertyChangeListener(internalListener);
return panel;
}
void setValue(T value) {
originalValue = value;
editor.removePropertyChangeListener(internalListener);
editor.setValue(value);
editor.addPropertyChangeListener(internalListener);
}
@Override
protected void okCallback() {
close();
dialog = null;
}
@Override
protected void cancelCallback() {
valueChanged(originalValue);
close();
dialog = null;
}
}
}

View File

@ -85,16 +85,16 @@ public class LookAndFeelInstaller {
}
/**
* Installs GColors into the UIDefaults. Subclasses my override this if they need to install
* GColors in a different way.
* Installs Colors, Fonts, and Icons into the UIDefaults. Subclasses my override this if they need to install
* UI properties in a different way.
*/
protected void installJavaDefaults() {
GThemeValueMap javaDefaults = extractJavaDefaults();
Gui.setJavaDefaults(javaDefaults);
installIndirectValues(javaDefaults);
installPropertiesBackIntoUiDefaults(javaDefaults);
}
private void installIndirectValues(GThemeValueMap javaDefaults) {
private void installPropertiesBackIntoUiDefaults(GThemeValueMap javaDefaults) {
UIDefaults defaults = UIManager.getDefaults();
for (ColorValue colorValue : javaDefaults.getColors()) {
String id = colorValue.getId();
@ -103,25 +103,43 @@ public class LookAndFeelInstaller {
}
for (FontValue fontValue : javaDefaults.getFonts()) {
String id = fontValue.getId();
GFont gFont = new GFont(id);
if (!gFont.equals(fontValue.getRawValue())) {
// only update if we have changed the default java color
defaults.put(id, gFont);
}
//Note: fonts don't support indirect values, so there is no GFont object
Font font = Gui.getFont(id);
defaults.put(id, font);
}
// for (IconValue iconValue : javaDefaults.getIcons()) {
// String id = iconValue.getId();
// GIconUIResource gIcon = Gui.getGIconUiResource(id);
// defaults.put(id, gIcon);
// }
}
protected GThemeValueMap extractJavaDefaults() {
return extractJavaDefaults(UIManager.getDefaults());
}
protected static GThemeValueMap extractJavaDefaults(UIDefaults defaults) {
GThemeValueMap values = new GThemeValueMap();
// for now, just doing color properties.
List<String> ids =
LookAndFeelUtils.getLookAndFeelIdsForType(UIManager.getDefaults(), Color.class);
List<String> ids = LookAndFeelUtils.getLookAndFeelIdsForType(defaults, Color.class);
for (String id : ids) {
// only use standard java colors here to avoid weird issues (such as GColor not
// resolving or ColorUIResource not being honored. Later we will go back
// and fix up the java defaults to use standard java color indirection
values.addColor(new ColorValue(id, getNormalizedColor(UIManager.getColor(id))));
}
ids = LookAndFeelUtils.getLookAndFeelIdsForType(defaults, Font.class);
for (String id : ids) {
values.addFont(new FontValue(id, UIManager.getFont(id)));
}
ids = LookAndFeelUtils.getLookAndFeelIdsForType(defaults, Icon.class);
for (String id : ids) {
Icon icon = UIManager.getIcon(id);
Msg.debug(LookAndFeelInstaller.class,
"adding " + id + " icon class = " + icon.getClass().getName());
values.addIcon(new IconValue(id, icon));
}
return values;
}
@ -271,9 +289,13 @@ public class LookAndFeelInstaller {
String id = colorValue.getId();
defaults.put(id, null);
}
// for (FontValue fontValue : javaDefaults.getFonts()) {
// String id = fontValue.getId();
// defaults.put(id, null);
// }
for (FontValue fontValue : javaDefaults.getFonts()) {
String id = fontValue.getId();
defaults.put(id, null);
}
for (IconValue iconValue : javaDefaults.getIcons()) {
String id = iconValue.getId();
defaults.put(id, null);
}
}
}

View File

@ -15,8 +15,7 @@
*/
package docking.theme.laf;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.*;
import java.util.List;
import javax.swing.*;
@ -24,6 +23,7 @@ import javax.swing.plaf.nimbus.NimbusLookAndFeel;
import docking.theme.*;
import ghidra.docking.util.LookAndFeelUtils;
import ghidra.util.Msg;
public class NimbusLookAndFeelInstaller extends LookAndFeelInstaller {
@ -71,13 +71,37 @@ public class NimbusLookAndFeelInstaller extends LookAndFeelInstaller {
ColorValue value = new ColorValue(id, color);
javaDefaults.addColor(value);
}
List<String> fontIds = LookAndFeelUtils.getLookAndFeelIdsForType(defaults, Font.class);
for (String id : fontIds) {
Font font = defaults.getFont(id);
FontValue value = new FontValue(id, font);
javaDefaults.addFont(value);
}
List<String> iconIds = LookAndFeelUtils.getLookAndFeelIdsForType(defaults, Icon.class);
Msg.debug(LookAndFeelInstaller.class, "Icons found: " + iconIds.size());
for (String id : iconIds) {
Icon icon = defaults.getIcon(id);
javaDefaults.addIcon(new IconValue(id, icon));
}
Gui.setJavaDefaults(javaDefaults);
for (String id : colorIds) {
defaults.put(id, Gui.getGColorUiResource(id));
}
// for (String id : iconIds) {
// GIconUIResource icon = Gui.getGIconUiResource(id);
// if (icon.getId().equals("Menu.arrowIcon")) {
// defaults.put(id, new IconWrappedImageIcon(Gui.getRawIcon(id, false)));
// }
// else {
// defaults.put(id, Gui.getGIconUiResource(id));
// }
// }
// javaDefaults.addColor(new ColorValue("Label.textForground", "Label.foreground"));
defaults.put("Label.textForeground", Gui.getGColorUiResource("Label.foreground"));
GColor.refreshAll();
GIcon.refreshAll();
return defaults;
}

View File

@ -49,6 +49,7 @@ public class WrappingLookAndFeel extends LookAndFeel {
}
defaults.put("Label.textForeground", Gui.getGColorUiResource("Label.foreground"));
GColor.refreshAll();
GIcon.refreshAll();
return defaults;
}

View File

@ -23,6 +23,7 @@ import javax.swing.ImageIcon;
import generic.Images;
import resources.ResourceManager;
import resources.icons.UnresolvedIcon;
/**
* Container class for an icon and its location. If the location is
@ -76,7 +77,7 @@ public class ToolIconURL implements Comparable<ToolIconURL> {
// is it absolute, or in resources by the given path?
baseIcon = ResourceManager.loadImage(iconLocation);
if (baseIcon == ResourceManager.getDefaultIcon()) {
if (baseIcon instanceof UnresolvedIcon) {
// ...must not be, look for it in our 'special' locations
baseIcon = loadFromKnownImageResources(iconLocation);
}
@ -143,8 +144,7 @@ public class ToolIconURL implements Comparable<ToolIconURL> {
return image;
}
return ResourceManager.getScaledIcon(unscaledIcon, SMALL_ICON_SIZE,
SMALL_ICON_SIZE);
return ResourceManager.getScaledIcon(unscaledIcon, SMALL_ICON_SIZE, SMALL_ICON_SIZE);
}
private ImageIcon getLargeIcon(ImageIcon unscaledIcon) {
@ -167,12 +167,11 @@ public class ToolIconURL implements Comparable<ToolIconURL> {
// 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 == ResourceManager.getDefaultIcon()) {
if (unscaledIcon instanceof UnresolvedIcon) {
return ResourceManager.loadImage(Images.BIG_BOMB);
}
return ResourceManager.getScaledIcon(unscaledIcon, LARGE_ICON_SIZE,
LARGE_ICON_SIZE);
return ResourceManager.getScaledIcon(unscaledIcon, LARGE_ICON_SIZE, LARGE_ICON_SIZE);
}
private ImageIcon findCompatibleImageForSize(String imagePath, int desiredSize) {
@ -191,11 +190,7 @@ public class ToolIconURL implements Comparable<ToolIconURL> {
name += location.substring(dotIndex);
}
ImageIcon image = getImageIcon(name);
if (image != null) {
return image;
}
return null;
return getImageIcon(name);
}
private String stripSizeOffName(String name) {
@ -218,11 +213,7 @@ public class ToolIconURL implements Comparable<ToolIconURL> {
private ImageIcon getImageIcon(String name) {
ImageIcon image = ResourceManager.loadImage(name);
ImageIcon defaultIcon = ResourceManager.getDefaultIcon();
if (image == defaultIcon) {
if (!name.startsWith("images")) {
return getImageIcon("images/" + name);
}
if (image instanceof UnresolvedIcon) {
return null;
}
return image;
@ -289,16 +280,8 @@ public class ToolIconURL implements Comparable<ToolIconURL> {
* @param name name of the icon
*/
private ImageIcon loadFromKnownImageResources(String name) {
// first look in special location for tool icons
String filename = "defaultTools/images/" + name;
ImageIcon image = ResourceManager.loadImage(filename);
// if we can't find the icon in the special tool icon location, then look in general images.
if (image == ResourceManager.getDefaultIcon()) {
filename = "images/" + name;
image = ResourceManager.loadImage(filename);
}
return image;
return ResourceManager.loadImage(filename);
}
private void checkAnimated(ImageIcon imgIcon) {

View File

@ -27,7 +27,7 @@ public class DefaultDropDownSelectionDataModel<T> implements DropDownTextFieldDa
protected List<T> data;
private Comparator<Object> comparator;
protected Comparator<Object> comparator;
private DataToStringConverter<T> searchConverter;
private DataToStringConverter<T> descriptionConverter;
private ListCellRenderer<T> renderer =

View File

@ -115,8 +115,8 @@ public class ColumnFilterArchiveDialog<R> extends DialogComponentProvider {
private JComponent buildFilterList() {
JPanel panel = new JPanel(new BorderLayout());
panel.setBorder(BorderFactory.createTitledBorder(
BorderFactory.createEmptyBorder(19, 0, 0, 5), "Filter Names"));
panel.setBorder(BorderFactory
.createTitledBorder(BorderFactory.createEmptyBorder(19, 0, 0, 5), "Filter Names"));
jList = new JList<>();
jList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
@ -136,22 +136,22 @@ public class ColumnFilterArchiveDialog<R> extends DialogComponentProvider {
}
private JComponent buildActionPanel() {
ImageIcon icon = Icons.DELETE_ICON;
Icon icon = Icons.DELETE_ICON;
removeSelectedFiltersButton = new JButton("Remove", icon);
removeSelectedFiltersButton.setEnabled(false);
removeSelectedFiltersButton.addActionListener(e -> removeSelectedFilter());
JPanel buttonPanel = new JPanel(new BorderLayout());
buttonPanel.add(removeSelectedFiltersButton, BorderLayout.EAST);
JPanel panel = new JPanel(new BorderLayout());
panel.add(removeSelectedFiltersButton, BorderLayout.EAST);
return buttonPanel;
return panel;
}
private Component buildPreviewPanel() {
JPanel panel = new JPanel(new BorderLayout());
panel.setBorder(BorderFactory.createTitledBorder(
BorderFactory.createEmptyBorder(19, 0, 26, 5), "Preview"));
panel.setBorder(BorderFactory
.createTitledBorder(BorderFactory.createEmptyBorder(19, 0, 26, 5), "Preview"));
previewLabel = new GDHtmlLabel();
previewLabel.setVerticalAlignment(SwingConstants.TOP);

View File

@ -93,7 +93,7 @@ public class ConstraintFilterPanel extends JPanel {
private Component buildButtonPanel() {
JPanel panel = new JPanel(new BorderLayout());
ImageIcon icon = Icons.DELETE_ICON;
Icon icon = Icons.DELETE_ICON;
JButton button = new EmptyBorderButton(icon);
button.setToolTipText("Delete entry");

View File

@ -488,7 +488,7 @@ public class TaskMonitorComponent extends JPanel implements TaskMonitor {
mainContentPanel.add(progressBarPanel, BorderLayout.CENTER);
mainContentPanel.add(progressPanel, BorderLayout.EAST);
ImageIcon icon = Icons.STOP_ICON;
Icon icon = Icons.STOP_ICON;
cancelButton = new EmptyBorderButton(icon);
cancelButton.setName("CANCEL_TASK");

View File

@ -23,72 +23,74 @@ import java.net.URL;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import docking.theme.GIcon;
import ghidra.util.Msg;
import resources.icons.*;
import resources.icons.RotateIcon;
import resources.icons.TranslateIcon;
/**
* A class to get generic icons for standard actions. All methods in this class return an
* A class to get generic icons for standard actions. All methods in this class return an
* icon that is 16x16 unless the method name ends in another size.'
*/
public class Icons {
public static final ImageIcon EMPTY_ICON = get("images/EmptyIcon16.gif");
public static final Icon EMPTY_ICON = new GIcon("icon.empty");
public static final ImageIcon HELP_ICON = get("images/help-browser.png");
public static final Icon HELP_ICON = new GIcon("icon.help");
public static final ImageIcon ADD_ICON = get("images/Plus2.png");
public static final Icon ADD_ICON = new GIcon("icon.add");
public static final ImageIcon COLLAPSE_ALL_ICON = get("images/collapse_all.png");
public static final ImageIcon EXPAND_ALL_ICON = get("images/expand_all.png");
public static final Icon COLLAPSE_ALL_ICON = new GIcon("icon.collapse.all");
public static final Icon EXPAND_ALL_ICON = new GIcon("icon.expand.all");
public static final ImageIcon CONFIGURE_FILTER_ICON = get("images/exec.png");
public static final ImageIcon DELETE_ICON = get("images/error.png");
public static final ImageIcon ERROR_ICON = get("images/emblem-important.png");
public static final Icon CONFIGURE_FILTER_ICON = new GIcon("icon.configure.filter");
public static final Icon DELETE_ICON = new GIcon("icon.delete");
public static final Icon ERROR_ICON = new GIcon("icon.error");
public static final ImageIcon NAVIGATE_ON_INCOMING_EVENT_ICON = get("images/locationIn.gif");
public static final ImageIcon NAVIGATE_ON_OUTGOING_EVENT_ICON = get("images/locationOut.gif");
public static final Icon NAVIGATE_ON_INCOMING_EVENT_ICON = new GIcon("icon.navigate.in");
public static final Icon NAVIGATE_ON_OUTGOING_EVENT_ICON = new GIcon("icon.navigate.out");
public static final ImageIcon NOT_ALLOWED_ICON = get("images/no.png");
public static final ImageIcon OPEN_FOLDER_ICON = get("images/openSmallFolder.png");
public static final ImageIcon REFRESH_ICON = get("images/reload3.png");
public static final Icon NOT_ALLOWED_ICON = new GIcon("icon.notallowed");
public static final Icon OPEN_FOLDER_ICON = new GIcon("icon.folder.open");
public static final Icon REFRESH_ICON = new GIcon("icon.refresh");
public static final ImageIcon SORT_ASCENDING_ICON = get("images/sortascending.png");
public static final ImageIcon SORT_DESCENDING_ICON = get("images/sortdescending.png");
public static final Icon SORT_ASCENDING_ICON = new GIcon("icon.sort.ascending");
public static final Icon SORT_DESCENDING_ICON = new GIcon("icon.sort.descending");
public static final ImageIcon STOP_ICON = get("images/process-stop.png");
public static final ImageIcon STRONG_WARNING_ICON = get("images/software-update-urgent.png");
public static final Icon STOP_ICON = new GIcon("icon.stop");
public static final Icon STRONG_WARNING_ICON = new GIcon("icon.warning.strong");
public static final ImageIcon LEFT_ICON = get("images/left.png");
public static final ImageIcon RIGHT_ICON = get("images/right.png");
public static final Icon LEFT_ICON = new GIcon("icon.left");
public static final Icon RIGHT_ICON = new GIcon("icon.right");
/** An version of the LEFT_ICON with a different color */
public static final ImageIcon LEFT_ALTERNATE_ICON = get("images/left.alternate.png");
public static final Icon LEFT_ALTERNATE_ICON = new GIcon("icon.left.alt");
/** An version of the RIGHT_ICON with a different color */
public static final ImageIcon RIGHT_ALTERNATE_ICON = get("images/right.alternate.png");
public static final Icon RIGHT_ALTERNATE_ICON = new GIcon("icon.right.alt");
public static final ImageIcon SAVE_AS =
ResourceManager.getImageIcon(new DotDotDotIcon(get("images/Disk.png")));
public static final Icon SAVE_AS =
ResourceManager.getImageIcon(new DotDotDotIcon(new GIcon("icon.saveas")));
public static final ImageIcon MAKE_SELECTION_ICON = get("images/text_align_justify.png");
public static final Icon MAKE_SELECTION_ICON = new GIcon("icon.makeselection");
// Not necessarily re-usable, but this is needed for the help system; these should
// Not necessarily re-usable, but this is needed for the help system; these should
// probably be moved to the client that uses them, while updating the
// help system to use them there.
public static final ImageIcon ARROW_DOWN_RIGHT_ICON =
ResourceManager.getImageIcon(new RotateIcon(get("images/viewmagfit.png"), 90));
public static final ImageIcon ARROW_UP_LEFT_ICON =
ResourceManager.getImageIcon(new RotateIcon(get("images/viewmagfit.png"), 275));
public static final ImageIcon FILTER_NOT_ACCEPTED_ICON =
ResourceManager.getImageIcon(new MultiIcon(get("images/flag.png"), new TranslateIcon(
ResourceManager.loadImage("images/dialog-cancel.png", 10, 10), 6, 6)));
public static final ImageIcon APPLY_BLOCKED_MATCH_ICON =
ResourceManager.getImageIcon(new MultiIcon(get("images/kgpg.png"), new TranslateIcon(
ResourceManager.loadImage("images/checkmark_green.gif", 12, 12), 4, 0)));
public static final Icon ARROW_DOWN_RIGHT_ICON =
ResourceManager.getImageIcon(new RotateIcon(new GIcon("icon.arrow.up.right"), 90));
public static final Icon ARROW_UP_LEFT_ICON =
ResourceManager.getImageIcon(new RotateIcon(new GIcon("icon.arrow.up.right"), 275));
public static final Icon FILTER_NOT_ACCEPTED_ICON =
ResourceManager.getImageIcon(new MultiIcon(new GIcon("icon.flag"),
new TranslateIcon(ResourceManager.loadImage("icon.notallowed", 10, 10), 6, 6)));
public static final Icon APPLY_BLOCKED_MATCH_ICON =
ResourceManager.getImageIcon(new MultiIcon(new GIcon("icon.lock"),
new TranslateIcon(ResourceManager.loadImage("icon.checkmark.green", 12, 12), 4, 0)));
/**
* Returns true if the given string is a Java code snippet that references this class
*
*
* @param snippet the string to check
* @return true if the given string is a Java code snippet that references this class
*/
@ -97,9 +99,9 @@ public class Icons {
}
/**
* Returns an {@link IconProvider} for the given string value, which is usually the 'src'
* attribute of an IMG tag
*
* Returns an {@link IconProvider} for the given string value, which is usually the 'src'
* attribute of an IMG tag
*
* @param snippet the snippet
* @return the icon provider
*/
@ -123,12 +125,12 @@ public class Icons {
* Gets the icon for the given icon path. The given path should be relative to the classpath.
* If an icon by that name can't be found, the default "bomb" icon is returned instead.
* <P>
* For example, an icon named foo.png would typically be stored in the module at
* For example, an icon named foo.png would typically be stored in the module at
* "{modulePath}/src/main/resources/image/foo.png". To reference that icon, use the path
* "images/foo.png", since "{modulePath}/src/main/resources" is in the classpath.
*
*
* @param iconPath the icon path (relative to the classpath)
* @return The icon referenced by that path.
* @return The icon referenced by that path.
*/
public static ImageIcon get(String iconPath) {
return ResourceManager.loadImage(iconPath);
@ -139,14 +141,14 @@ public class Icons {
* The given path should be relative to the classpath.
* If an icon by that name can't be found, the default "bomb" icon is returned instead.
* <P>
* For example, an icon named foo.png would typically be stored in the module at
* For example, an icon named foo.png would typically be stored in the module at
* "{modulePath}/src/main/resources/image/foo.png". To reference that icon, use the path
* "images/foo.png", since "{modulePath}/src/main/resources" is in the classpath.
*
*
* @param iconPath the icon path (relative to the classpath)
* @param width the desired width after scaling
* @param height the desired height after scaling
* @return The icon referenced by that path.
* @return The icon referenced by that path.
*/
public static ImageIcon get(String iconPath, int width, int height) {
return ResourceManager.loadImage(iconPath, width, height);
@ -181,10 +183,6 @@ public class Icons {
return null;
}
if (icon instanceof UrlImageIcon) {
return ((UrlImageIcon) icon).getUrl();
}
// Note: we embed the icon's URL in its description
String description = icon.getDescription();
if (description == null) {

View File

@ -22,10 +22,13 @@ import java.awt.Font;
import java.io.File;
import java.io.IOException;
import javax.swing.Icon;
import org.junit.Before;
import org.junit.Test;
import generic.test.AbstractGenericTest;
import resources.ResourceManager;
public class GThemeTest extends AbstractGenericTest {
@ -34,6 +37,8 @@ public class GThemeTest extends AbstractGenericTest {
private static final Color COLOR_WITH_ALPHA = new Color(10, 20, 30, 40);
private static final String ICON_PATH_1 = "images/arrow.png";
private static final String ICON_PATH_2 = "images/disk.png";
private static final Icon ICON1 = ResourceManager.loadImage(ICON_PATH_1);
private static final Icon ICON2 = ResourceManager.loadImage(ICON_PATH_2);
private GTheme theme;
@ -64,8 +69,8 @@ public class GThemeTest extends AbstractGenericTest {
@Test
public void testSetIconPath() {
theme.setIcon("icon.a.1", ICON_PATH_1);
assertEquals(ICON_PATH_1, theme.getIcon("icon.a.1").get(null));
theme.setIcon("icon.a.1", ICON1);
assertEquals(ICON1, theme.getIcon("icon.a.1").get(null));
}
@Test
@ -84,10 +89,10 @@ public class GThemeTest extends AbstractGenericTest {
theme.setFont("x.y.z", COURIER);
theme.setFontRef("x.y.z.1", "x.y.z");
theme.setIcon("icon.a.1", ICON_PATH_1);
theme.setIcon("icon.a.2", ICON_PATH_2);
theme.setIcon("icon.a.1", ICON1);
theme.setIcon("icon.a.2", ICON2);
theme.setIconRef("icon.a.3", "icon.a.1");
theme.setIcon("t.u.v", ICON_PATH_1);
theme.setIcon("t.u.v", ICON1);
theme.setIconRef("t.u.v.1", "t.u.v");
File file = createTempFile("themeTest.theme");

View File

@ -473,6 +473,27 @@ public class ResourceManager {
return getScaledIcon(loadImage, width, height);
}
/**
* Attempts to load an icon from the given path. Returns the icon or null if no icon was
* found from the given path.
* <p>
*
* @param path the icon to load, e.g., "images/home.gif"
* @return the ImageIcon if it exists or null
*/
public static ImageIcon findIcon(String path) {
// use the wrapper so that images are not loaded until they are needed
ImageIcon icon = iconMap.get(path);
if (icon == null) {
icon = doLoadIcon(path);
if (icon != null) {
iconMap.put(path, icon);
}
}
return icon;
}
/**
* Load the image specified by filename; returns the default bomb icon
* if problems occur trying to load the file.
@ -483,13 +504,27 @@ public class ResourceManager {
public static ImageIcon loadImage(String filename) {
ImageIcon icon = iconMap.get(filename);
if (icon == null) {
icon = doLoadIcon(filename, ResourceManager.getDefaultIcon());
icon = doLoadIcon(filename);
if (icon == null) {
icon = new UnresolvedIcon(filename, getDefaultIcon());
}
iconMap.put(filename, icon);
}
return icon;
}
private static ImageIcon doLoadIcon(String filename, ImageIcon defaultIcon) {
public static Set<Icon> getLoadedUrlIcons() {
Set<Icon> icons = new HashSet<>();
for (Icon icon : iconMap.values()) {
if (icon instanceof UrlImageIcon) {
icons.add(icon);
}
}
return icons;
}
private static ImageIcon doLoadIcon(String filename) {
// if only the name of an icon is given, but not a path, check to see if it is
// a resource that lives under our "images/" folder
if (!filename.contains("/")) {
@ -516,7 +551,7 @@ public class ResourceManager {
}
}
return defaultIcon;
return null;
}
/**

View File

@ -0,0 +1,29 @@
/* ###
* 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 resources.icons;
import javax.swing.ImageIcon;
/**
* Icon class for when we can't find an icon for a path
*/
public class UnresolvedIcon extends DerivedImageIcon {
public UnresolvedIcon(String path, ImageIcon icon) {
super("Unresolved: " + path, icon.getImage());
}
}

View File

@ -30,6 +30,7 @@ import ghidra.util.Msg;
* {@link LazyImageIcon} that is created from a URL to an icon file.
*/
public class UrlImageIcon extends LazyImageIcon {
private String originalPath;
private URL imageUrl;
/**
@ -38,14 +39,27 @@ public class UrlImageIcon extends LazyImageIcon {
* @param url the {@link URL} to an icon resource file
*/
public UrlImageIcon(String path, URL url) {
super(path);
super(url.toExternalForm());
this.originalPath = Objects.requireNonNull(path);
this.imageUrl = Objects.requireNonNull(url);
}
/**
* Returns the URL that was used to create this icon
* @return the URL that was used to create this icon
*/
public URL getUrl() {
return imageUrl;
}
/**
* Returns the original path that was used to generate the URL (e.g. images/foo.png)
* @return the original path that was used to generate the URL (e.g. images/foo.png)
*/
public String getOriginalPath() {
return originalPath;
}
@Override
protected ImageIcon createImageIcon() {
String name = getFilename();

View File

@ -254,7 +254,7 @@ public class GProgressBar extends JPanel {
activeProgressPanel.add(imageLabel, BorderLayout.EAST);
}
ImageIcon icon = Icons.STOP_ICON;
Icon icon = Icons.STOP_ICON;
cancelButton = new EmptyBorderButton(icon);
cancelButton.setName("CANCEL_TASK");