mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-11-28 15:11:44 +00:00
GP-1981 Refactored Gui to use ThemeManager
This commit is contained in:
parent
a92a27e9f1
commit
edfb5a0877
@ -212,10 +212,10 @@ public interface AutoOptions {
|
||||
else {
|
||||
options.registerOption(key.getName(), type, defaultValue, help, description,
|
||||
editor);
|
||||
// TODO: Wish Ghidra would do this upon any option registration
|
||||
options.putObject(key.getName(), defaultValue, type);
|
||||
}
|
||||
|
||||
// TODO: Wish Ghidra would do this upon any option registration
|
||||
options.putObject(key.getName(), defaultValue, type);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -68,6 +68,8 @@ color.fg.plugin.comments.history.text = blue
|
||||
color.fg.plugin.comments.history.user = color.fg
|
||||
color.fg.plugin.comments.history.date = rgb(124, 37, 18)
|
||||
|
||||
color.bg.plugin.programtree = color.bg
|
||||
|
||||
color.bg.plugin.datamgr.edge.default = blue
|
||||
color.bg.plugin.datamgr.edge.composite = magenta
|
||||
color.bg.plugin.datamgr.edge.reference = blue
|
||||
|
@ -37,6 +37,6 @@
|
||||
|
||||
icon.fsbrowser.file.substring.release. = images/famfamfam_silk_icons_v013/bullet_purple.png
|
||||
|
||||
icon.fsbrowser.file.overlay.imported = images/checkmark_green.gif
|
||||
icon.fsbrowser.file.overlay.filesystem = EMPTY_ICON{images/nuvola/16x16/ledgreen.png[size(8,8)][move(8,8)]} // lower right quadrant
|
||||
icon.fsbrowser.file.overlay.imported = EMPTY_ICON{images/checkmark_green.gif[size(8,8)][move(8,8)]} // lower right quadrant
|
||||
icon.fsbrowser.file.overlay.filesystem = EMPTY_ICON{images/nuvola/16x16/ledgreen.png[size(8,8)][move(0,8)]} // lower left quadrant
|
||||
icon.fsbrowser.file.overlay.missing.password = EMPTY_ICON{images/lock.png[size(8,8)][move(8,0)]} // upper right quadrant
|
@ -38,7 +38,6 @@ import ghidra.program.database.ProgramDB;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.exception.UsrException;
|
||||
import ghidra.util.task.TaskLauncher;
|
||||
import ghidra.util.task.TaskMonitorAdapter;
|
||||
|
||||
/**
|
||||
* Main Ghidra application class. Creates
|
||||
@ -73,7 +72,6 @@ public class GhidraRun implements GhidraLaunchable {
|
||||
Runnable mainTask = () -> {
|
||||
|
||||
GhidraApplicationConfiguration configuration = new GhidraApplicationConfiguration();
|
||||
configuration.setTaskMonitor(new StatusReportingTaskMonitor());
|
||||
Application.initializeApplication(layout, configuration);
|
||||
|
||||
log = LogManager.getLogger(GhidraRun.class);
|
||||
@ -243,15 +241,3 @@ public class GhidraRun implements GhidraLaunchable {
|
||||
// this exists just to allow access to the constructor
|
||||
}
|
||||
}
|
||||
|
||||
class StatusReportingTaskMonitor extends TaskMonitorAdapter {
|
||||
@Override
|
||||
public synchronized void setCancelEnabled(boolean enable) {
|
||||
// Not permitted
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMessage(String message) {
|
||||
SplashScreen.updateSplashScreenStatus(message);
|
||||
}
|
||||
}
|
||||
|
@ -33,8 +33,7 @@ import docking.actions.KeyBindingUtils;
|
||||
import docking.options.editor.FontEditor;
|
||||
import docking.widgets.OptionDialog;
|
||||
import docking.widgets.filechooser.GhidraFileChooser;
|
||||
import generic.theme.GIcon;
|
||||
import generic.theme.Gui;
|
||||
import generic.theme.*;
|
||||
import ghidra.framework.plugintool.ComponentProviderAdapter;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.Msg;
|
||||
@ -274,7 +273,7 @@ public class TextEditorComponentProvider extends ComponentProviderAdapter {
|
||||
FontEditor editor = new FontEditor();
|
||||
editor.setValue(Gui.getFont(FONT_ID));
|
||||
editor.showDialog();
|
||||
Gui.setFont(FONT_ID, (Font) editor.getValue());
|
||||
ThemeManager.getInstance().setFont(FONT_ID, (Font) editor.getValue());
|
||||
}
|
||||
|
||||
private void save() {
|
||||
|
@ -34,6 +34,8 @@ import resources.ResourceManager;
|
||||
* Cell renderer for the drag and drop tree.
|
||||
*/
|
||||
class DnDTreeCellRenderer extends DefaultTreeCellRenderer {
|
||||
private static final Color BACKGROUND_UNSELECTED = new GColor("color.bg.tree");
|
||||
private static final Color BACKGROUND_SELECTED = new GColor("color.bg.tree.selected");
|
||||
|
||||
private static final String DISABLED_DOCS = "DisabledDocument.gif";
|
||||
private static final String DISABLED_FRAGMENT = "DisabledFragment";
|
||||
@ -73,8 +75,8 @@ class DnDTreeCellRenderer extends DefaultTreeCellRenderer {
|
||||
*/
|
||||
DnDTreeCellRenderer() {
|
||||
super();
|
||||
defaultNonSelectionColor = new GColor("Tree.textBackground");
|
||||
defaultSelectionColor = new GColor("Tree.selectionBackground");
|
||||
defaultNonSelectionColor = BACKGROUND_UNSELECTED;
|
||||
defaultSelectionColor = BACKGROUND_SELECTED;
|
||||
rowForFeedback = -1;
|
||||
|
||||
// disable HTML rendering
|
||||
|
@ -70,6 +70,7 @@ public abstract class DragNDropTree extends JTree implements Draggable, Droppabl
|
||||
|
||||
public DragNDropTree(DefaultTreeModel model) {
|
||||
super(model);
|
||||
setBackground(new GColor("color.bg.tree"));
|
||||
treeModel = model;
|
||||
this.root = (ProgramNode) model.getRoot();
|
||||
//setEditable(true); // edit interferes with drag gesture listener
|
||||
|
@ -315,8 +315,9 @@ public class ProgramDnDTree extends DragNDropTree {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
Object data = e.getTransferable().getTransferData(
|
||||
SelectionTransferable.localProgramSelectionFlavor);
|
||||
Object data = e.getTransferable()
|
||||
.getTransferData(
|
||||
SelectionTransferable.localProgramSelectionFlavor);
|
||||
SelectionTransferData transferData = (SelectionTransferData) data;
|
||||
return program.getDomainFile().getPathname().equals(transferData.getProgramPath());
|
||||
}
|
||||
|
@ -31,9 +31,8 @@ import docking.actions.KeyBindingUtils;
|
||||
import docking.options.editor.FontEditor;
|
||||
import docking.widgets.OptionDialog;
|
||||
import generic.jar.ResourceFile;
|
||||
import generic.theme.GIcon;
|
||||
import generic.theme.*;
|
||||
import generic.theme.GThemeDefaults.Colors;
|
||||
import generic.theme.Gui;
|
||||
import ghidra.app.script.GhidraScriptUtil;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.datastruct.FixedSizeStack;
|
||||
@ -508,7 +507,7 @@ public class GhidraScriptEditorComponentProvider extends ComponentProvider {
|
||||
FontEditor editor = new FontEditor();
|
||||
editor.setValue(Gui.getFont(FONT_ID));
|
||||
editor.showDialog();
|
||||
Gui.setFont(FONT_ID, (Font) editor.getValue());
|
||||
ThemeManager.getInstance().setFont(FONT_ID, (Font) editor.getValue());
|
||||
}
|
||||
|
||||
private void save() {
|
||||
|
@ -20,7 +20,7 @@ import docking.DockingWindowManager;
|
||||
import docking.framework.ApplicationInformationDisplayFactory;
|
||||
import docking.framework.SplashScreen;
|
||||
import docking.widgets.PopupKeyStorePasswordProvider;
|
||||
import generic.theme.Gui;
|
||||
import generic.theme.ApplicationThemeManager;
|
||||
import ghidra.docking.util.LookAndFeelUtils;
|
||||
import ghidra.formats.gfilesystem.crypto.CryptoProviders;
|
||||
import ghidra.formats.gfilesystem.crypto.PopupGUIPasswordProvider;
|
||||
@ -30,6 +30,7 @@ import ghidra.framework.preferences.Preferences;
|
||||
import ghidra.net.ApplicationKeyManagerFactory;
|
||||
import ghidra.util.ErrorDisplay;
|
||||
import ghidra.util.SystemUtilities;
|
||||
import ghidra.util.task.TaskMonitorAdapter;
|
||||
|
||||
public class GhidraApplicationConfiguration extends HeadlessGhidraApplicationConfiguration {
|
||||
|
||||
@ -43,12 +44,13 @@ public class GhidraApplicationConfiguration extends HeadlessGhidraApplicationCon
|
||||
|
||||
@Override
|
||||
protected void initializeApplication() {
|
||||
Gui.initialize();
|
||||
ApplicationThemeManager.initialize();
|
||||
LookAndFeelUtils.performPlatformSpecificFixups();
|
||||
|
||||
if (showSplashScreen) {
|
||||
showUserAgreement();
|
||||
SplashScreen.showSplashScreen();
|
||||
this.monitor = new StatusReportingTaskMonitor();
|
||||
}
|
||||
|
||||
super.initializeApplication();
|
||||
@ -89,4 +91,17 @@ public class GhidraApplicationConfiguration extends HeadlessGhidraApplicationCon
|
||||
public ErrorDisplay getErrorDisplay() {
|
||||
return new DockingErrorDisplay();
|
||||
}
|
||||
|
||||
private static class StatusReportingTaskMonitor extends TaskMonitorAdapter {
|
||||
@Override
|
||||
public synchronized void setCancelEnabled(boolean enable) {
|
||||
// Not permitted
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMessage(String message) {
|
||||
SplashScreen.updateSplashScreenStatus(message);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ public class FileIconService {
|
||||
}
|
||||
|
||||
private void createSubstringMap() {
|
||||
GThemeValueMap values = Gui.getAllValues();
|
||||
GThemeValueMap values = ThemeManager.getInstance().getCurrentValues();
|
||||
List<IconValue> icons = values.getIcons();
|
||||
for (IconValue iconValue : icons) {
|
||||
String id = iconValue.getId();
|
||||
|
@ -32,7 +32,7 @@ import org.junit.*;
|
||||
import docking.test.AbstractDockingTest;
|
||||
import generic.theme.GColor;
|
||||
import generic.theme.GThemeDefaults.Colors.Palette;
|
||||
import generic.theme.Gui;
|
||||
import generic.theme.ThemeManager;
|
||||
import ghidra.framework.options.*;
|
||||
import ghidra.framework.options.OptionsTest.FRUIT;
|
||||
import ghidra.program.database.ProgramBuilder;
|
||||
@ -61,7 +61,7 @@ public class OptionsDBTest extends AbstractDockingTest {
|
||||
ProgramDB program = builder.getProgram();
|
||||
txID = program.startTransaction("Test");
|
||||
options = new OptionsDB(program);
|
||||
Gui.setColor("color.test", Palette.MAGENTA);
|
||||
ThemeManager.getInstance().setColor("color.test", Palette.MAGENTA);
|
||||
testColor = new GColor("color.test");
|
||||
}
|
||||
|
||||
|
@ -33,7 +33,7 @@ public class TokenHighlightColors {
|
||||
float h = (float) Math.random(); // 0-360
|
||||
float s = .25f; // saturation; gray to full color; full color is too harsh for highlights
|
||||
float b = 1f; // brightness; black to full color
|
||||
if (Gui.getActiveTheme().useDarkDefaults()) {
|
||||
if (Gui.isDarkTheme()) {
|
||||
s = .5f; // a bit more color against a dark background
|
||||
b = .5f; // less brightness, as the background is not as bright
|
||||
}
|
||||
|
@ -92,7 +92,7 @@ src/main/resources/images/page_go.png||FAMFAMFAM Icons - CC 2.5||||END|
|
||||
src/main/resources/images/page_green.png||FAMFAMFAM Icons - CC 2.5||||END|
|
||||
src/main/resources/images/play.png||GHIDRA||||END|
|
||||
src/main/resources/images/preferences-system-windows.png||Tango Icons - Public Domain||||END|
|
||||
src/main/resources/images/redo.png||GHIDRA||||END|
|
||||
src/main/resources/images/redo.png||Crystal Clear Icons - LGPL 2.1||||END|
|
||||
src/main/resources/images/software-update-available.png||Tango Icons - Public Domain|||tango icon set|END|
|
||||
src/main/resources/images/table.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END|
|
||||
src/main/resources/images/tag.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END|
|
||||
@ -100,7 +100,7 @@ src/main/resources/images/text_lowercase.png||FAMFAMFAM Icons - CC 2.5|||famfamf
|
||||
src/main/resources/images/textfield_rename.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END|
|
||||
src/main/resources/images/tip.png||Oxygen Icons - LGPL 3.0|||Oxygen icon theme (dual license; LGPL or CC-SA-3.0)|END|
|
||||
src/main/resources/images/trash-empty.png||Oxygen Icons - LGPL 3.0|||Oxygen icon theme (dual license; LGPL or CC-SA-3.0)|END|
|
||||
src/main/resources/images/undo.png||GHIDRA||||END|
|
||||
src/main/resources/images/undo.png||Crystal Clear Icons - LGPL 2.1||||END|
|
||||
src/main/resources/images/user-home.png||Oxygen Icons - LGPL 3.0|||Oxygen icon theme (dual license; LGPL or CC-SA-3.0)|END|
|
||||
src/main/resources/images/view-filter.png||Oxygen Icons - LGPL 3.0|||Oxygen icon theme (dual license; LGPL or CC-SA-3.0)|END|
|
||||
src/main/resources/images/warning.help.png||Oxygen Icons - LGPL 3.0|||Oxygen icon theme (dual license; LGPL or CC-SA-3.0)|END|
|
||||
|
@ -82,12 +82,16 @@ color.border.provider.disconnected = orange
|
||||
|
||||
color.bg.filechooser = color.bg
|
||||
color.fg.filechooser = color.fg
|
||||
color.bg.filechooser.shortcut = lightGray
|
||||
|
||||
color.bg.fieldpanel = color.bg
|
||||
color.fg.fieldpanel = color.fg
|
||||
color.bg.fieldpanel.selection = color.bg.selection
|
||||
color.bg.fieldpanel.highlight = color.bg.highlight
|
||||
color.bg.fieldpanel.selection.and.highlight = green
|
||||
|
||||
color.bg.tree = [color]Tree.textBackground
|
||||
color.bg.tree.selected = [color]Tree.selectionBackground
|
||||
// docking buttons
|
||||
color.fg.button = black
|
||||
|
||||
@ -239,3 +243,10 @@ color.bg.tableheader.gradient.end.primary = darkBlue
|
||||
|
||||
// docking buttons
|
||||
color.fg.button = darkGray
|
||||
|
||||
color.bg.filechooser.shortcut = system.color.bg.widget
|
||||
|
||||
color.bg.tree = color.bg
|
||||
color.bg.tree.selected = [color]Tree.selectionBackground
|
||||
icon.undo = eatbits1.png
|
||||
icon.redo = eatbits2.png
|
||||
|
@ -23,6 +23,7 @@ import javax.swing.*;
|
||||
|
||||
import docking.action.*;
|
||||
import generic.theme.Gui;
|
||||
import generic.theme.ThemeManager;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.exception.AssertException;
|
||||
import help.HelpDescriptor;
|
||||
@ -801,7 +802,7 @@ public abstract class ComponentProvider implements HelpDescriptor, ActionContext
|
||||
else {
|
||||
size = Math.max(size - 1, 3);
|
||||
}
|
||||
Gui.setFont(registeredFontId, font.deriveFont((float) size));
|
||||
ThemeManager.getInstance().setFont(registeredFontId, font.deriveFont((float) size));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -38,7 +38,7 @@ import docking.widgets.list.GListCellRenderer;
|
||||
import docking.widgets.table.GTableCellRenderer;
|
||||
import docking.widgets.tree.support.GTreeRenderer;
|
||||
import generic.theme.GThemeDefaults.Colors.Palette;
|
||||
import generic.theme.Gui;
|
||||
import ghidra.docking.util.LookAndFeelUtils;
|
||||
import ghidra.util.HTMLUtilities;
|
||||
import resources.ResourceManager;
|
||||
|
||||
@ -126,7 +126,7 @@ public class DockingUtils {
|
||||
public static JSeparator createToolbarSeparator() {
|
||||
Dimension sepDim = new Dimension(2, ICON_SIZE + 2);
|
||||
JSeparator separator = new JSeparator(SwingConstants.VERTICAL);
|
||||
if (Gui.isUsingAquaUI(separator.getUI())) {
|
||||
if (LookAndFeelUtils.isUsingAquaUI(separator.getUI())) {
|
||||
separator.setUI(new BasicSeparatorUI());
|
||||
}
|
||||
separator.setPreferredSize(sepDim); // ugly work around to force height of separator
|
||||
|
@ -312,7 +312,7 @@ public class StatusBar extends JPanel {
|
||||
|
||||
int value = 0;
|
||||
int delta = 16;
|
||||
if (Gui.getActiveTheme().useDarkDefaults()) {
|
||||
if (Gui.isDarkTheme()) {
|
||||
value = 128;
|
||||
delta = -16;
|
||||
}
|
||||
|
@ -32,9 +32,9 @@ import ghidra.util.Msg;
|
||||
|
||||
public class ApplicationInformationDisplayFactory {
|
||||
|
||||
private static final GIcon ICON_HOME = new GIcon("icon.docking.application.home");
|
||||
private static final GIcon ICON_16 = new GIcon("icon.docking.application.16");
|
||||
private static final GIcon ICON_128 = new GIcon("icon.base.application.128");
|
||||
private static final String ICON_HOME = "icon.docking.application.home";
|
||||
private static final String ICON_16 = "icon.docking.application.16";
|
||||
private static final String ICON_128 = "icon.docking.application.128";
|
||||
|
||||
static {
|
||||
PluggableServiceRegistry.registerPluggableService(
|
||||
@ -144,13 +144,13 @@ public class ApplicationInformationDisplayFactory {
|
||||
}
|
||||
|
||||
protected Icon getSplashScreenIcon128() {
|
||||
return ICON_128;
|
||||
return new GIcon(ICON_128);
|
||||
}
|
||||
|
||||
protected List<Image> doGetWindowIcons() {
|
||||
List<Image> list = new ArrayList<>();
|
||||
list.add(ICON_128.getImageIcon().getImage());
|
||||
list.add(ICON_16.getImageIcon().getImage());
|
||||
list.add(new GIcon(ICON_128).getImageIcon().getImage());
|
||||
list.add(new GIcon(ICON_16).getImageIcon().getImage());
|
||||
return list;
|
||||
}
|
||||
|
||||
@ -163,7 +163,7 @@ public class ApplicationInformationDisplayFactory {
|
||||
}
|
||||
|
||||
protected Icon doGetHomeIcon() {
|
||||
return ICON_HOME;
|
||||
return new GIcon(ICON_HOME);
|
||||
}
|
||||
|
||||
protected Runnable doGetHomeCallback() {
|
||||
|
@ -17,7 +17,7 @@ package docking.framework;
|
||||
|
||||
import docking.DockingErrorDisplay;
|
||||
import docking.widgets.PopupKeyStorePasswordProvider;
|
||||
import generic.theme.Gui;
|
||||
import generic.theme.ApplicationThemeManager;
|
||||
import ghidra.docking.util.LookAndFeelUtils;
|
||||
import ghidra.framework.ApplicationConfiguration;
|
||||
import ghidra.net.ApplicationKeyManagerFactory;
|
||||
@ -49,7 +49,7 @@ public class DockingApplicationConfiguration extends ApplicationConfiguration {
|
||||
protected void initializeApplication() {
|
||||
super.initializeApplication();
|
||||
|
||||
Gui.initialize();
|
||||
ApplicationThemeManager.initialize();
|
||||
LookAndFeelUtils.performPlatformSpecificFixups();
|
||||
|
||||
if (showSplashScreen) {
|
||||
|
@ -24,7 +24,7 @@ import javax.swing.KeyStroke;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import docking.action.DockingActionIf;
|
||||
import generic.theme.Gui;
|
||||
import ghidra.docking.util.LookAndFeelUtils;
|
||||
import ghidra.util.StringUtilities;
|
||||
|
||||
class DockingToolBarUtils {
|
||||
@ -96,7 +96,7 @@ class DockingToolBarUtils {
|
||||
builder.append(InputEvent.getModifiersExText(modifiers));
|
||||
|
||||
// The Aqua LaF does not use the '+' symbol between modifiers
|
||||
if (!Gui.isUsingAquaUI(button.getUI())) {
|
||||
if (!LookAndFeelUtils.isUsingAquaUI(button.getUI())) {
|
||||
builder.append('+');
|
||||
}
|
||||
}
|
||||
|
@ -97,6 +97,16 @@ public class MultipleActionDockingToolbarButton extends EmptyBorderButton {
|
||||
popupContext = createPopupContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateUI() {
|
||||
|
||||
removeMouseListener(popupListener);
|
||||
|
||||
super.updateUI();
|
||||
|
||||
installMouseListeners();
|
||||
}
|
||||
|
||||
private void installMouseListeners() {
|
||||
MouseListener[] mouseListeners = getMouseListeners();
|
||||
for (MouseListener mouseListener : mouseListeners) {
|
||||
|
@ -43,9 +43,11 @@ public class ExportThemeDialog extends DialogComponentProvider {
|
||||
private JTextField fileTextField;
|
||||
private GCheckBox includeDefaultsCheckbox;
|
||||
private boolean exportAsZip;
|
||||
private ThemeManager themeManager;
|
||||
|
||||
public ExportThemeDialog(boolean exportAsZip) {
|
||||
public ExportThemeDialog(ThemeManager themeManager, boolean exportAsZip) {
|
||||
super("Export Theme");
|
||||
this.themeManager = themeManager;
|
||||
this.exportAsZip = exportAsZip;
|
||||
addWorkPanel(buildMainPanel());
|
||||
addOKButton();
|
||||
@ -62,7 +64,7 @@ public class ExportThemeDialog extends DialogComponentProvider {
|
||||
|
||||
private boolean exportTheme() {
|
||||
String themeName = nameField.getText();
|
||||
GTheme activeTheme = Gui.getActiveTheme();
|
||||
GTheme activeTheme = themeManager.getActiveTheme();
|
||||
LafType laf = activeTheme.getLookAndFeelType();
|
||||
boolean useDarkDefaults = activeTheme.useDarkDefaults();
|
||||
File file = new File(fileTextField.getText());
|
||||
@ -87,10 +89,10 @@ public class ExportThemeDialog extends DialogComponentProvider {
|
||||
|
||||
private void loadValues(GTheme exportTheme) {
|
||||
if (includeDefaultsCheckbox.isSelected()) {
|
||||
exportTheme.load(Gui.getAllValues());
|
||||
exportTheme.load(themeManager.getCurrentValues());
|
||||
}
|
||||
else {
|
||||
exportTheme.load(Gui.getNonDefaultValues());
|
||||
exportTheme.load(themeManager.getNonDefaultValues());
|
||||
}
|
||||
}
|
||||
|
||||
@ -114,7 +116,7 @@ public class ExportThemeDialog extends DialogComponentProvider {
|
||||
|
||||
private Component buildNameField() {
|
||||
nameField = new JTextField(25);
|
||||
nameField.setText(Gui.getActiveTheme().getName());
|
||||
nameField.setText(themeManager.getActiveTheme().getName());
|
||||
return nameField;
|
||||
}
|
||||
|
||||
@ -127,7 +129,7 @@ public class ExportThemeDialog extends DialogComponentProvider {
|
||||
private Component buildFilePanel() {
|
||||
File homeDir = new File(System.getProperty("user.home")); // prefer the home directory
|
||||
|
||||
String name = Gui.getActiveTheme().getName();
|
||||
String name = themeManager.getActiveTheme().getName();
|
||||
String filename = name.replaceAll(" ", "_") + ".";
|
||||
filename += exportAsZip ? GTheme.ZIP_FILE_EXTENSION : GTheme.FILE_EXTENSION;
|
||||
File file = new File(homeDir, filename);
|
||||
|
@ -37,7 +37,7 @@ public class IconValueEditor extends ThemeValueEditor<Icon> {
|
||||
|
||||
@Override
|
||||
protected Icon getRawValue(String id) {
|
||||
return Gui.getIcon(id, true);
|
||||
return Gui.getIcon(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -29,7 +29,7 @@ import docking.action.ActionContextProvider;
|
||||
import docking.widgets.table.GFilterTable;
|
||||
import docking.widgets.table.GTable;
|
||||
import generic.theme.ColorValue;
|
||||
import generic.theme.Gui;
|
||||
import generic.theme.ThemeManager;
|
||||
import ghidra.util.Swing;
|
||||
|
||||
/**
|
||||
@ -41,10 +41,12 @@ public class ThemeColorTable extends JPanel implements ActionContextProvider {
|
||||
private ColorValueEditor colorEditor = new ColorValueEditor(this::colorValueChanged);
|
||||
private GTable table;
|
||||
private GFilterTable<ColorValue> filterTable;
|
||||
private ThemeManager themeManager;
|
||||
|
||||
public ThemeColorTable() {
|
||||
public ThemeColorTable(ThemeManager themeManager) {
|
||||
super(new BorderLayout());
|
||||
colorTableModel = new ThemeColorTableModel();
|
||||
this.themeManager = themeManager;
|
||||
colorTableModel = new ThemeColorTableModel(themeManager);
|
||||
|
||||
filterTable = new GFilterTable<>(colorTableModel);
|
||||
table = filterTable.getTable();
|
||||
@ -87,7 +89,7 @@ public class ThemeColorTable extends JPanel implements ActionContextProvider {
|
||||
// run later - don't rock the boat in the middle of a listener callback
|
||||
Swing.runLater(() -> {
|
||||
ColorValue newValue = (ColorValue) event.getNewValue();
|
||||
Gui.setColor(newValue);
|
||||
themeManager.setColor(newValue);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -43,9 +43,11 @@ public class ThemeColorTableModel extends GDynamicColumnTableModel<ColorValue, O
|
||||
private GThemeValueMap defaultValues;
|
||||
private GThemeValueMap lightDefaultValues;
|
||||
private GThemeValueMap darkDefaultValues;
|
||||
private ThemeManager themeManager;
|
||||
|
||||
public ThemeColorTableModel() {
|
||||
public ThemeColorTableModel(ThemeManager themeManager) {
|
||||
super(new ServiceProviderStub());
|
||||
this.themeManager = themeManager;
|
||||
load();
|
||||
}
|
||||
|
||||
@ -53,7 +55,7 @@ public class ThemeColorTableModel extends GDynamicColumnTableModel<ColorValue, O
|
||||
* Reloads the just the current values shown in the table. Called whenever a color changes.
|
||||
*/
|
||||
public void reloadCurrent() {
|
||||
currentValues = Gui.getAllValues();
|
||||
currentValues = themeManager.getCurrentValues();
|
||||
colors = currentValues.getColors();
|
||||
fireTableDataChanged();
|
||||
}
|
||||
@ -67,20 +69,17 @@ public class ThemeColorTableModel extends GDynamicColumnTableModel<ColorValue, O
|
||||
fireTableDataChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the original value for the current theme
|
||||
*/
|
||||
public ColorValue getThemeValue(String id) {
|
||||
ColorValue getThemeValue(String id) {
|
||||
return themeValues.getColor(id);
|
||||
}
|
||||
|
||||
private void load() {
|
||||
currentValues = Gui.getAllValues();
|
||||
currentValues = themeManager.getCurrentValues();
|
||||
colors = currentValues.getColors();
|
||||
themeValues = Gui.getThemeValues();
|
||||
defaultValues = Gui.getDefaults();
|
||||
lightDefaultValues = Gui.getApplicationLightDefaults();
|
||||
darkDefaultValues = Gui.getApplicationDarkDefaults();
|
||||
themeValues = themeManager.getThemeValues();
|
||||
defaultValues = themeManager.getDefaults();
|
||||
lightDefaultValues = themeManager.getApplicationLightDefaults();
|
||||
darkDefaultValues = themeManager.getApplicationDarkDefaults();
|
||||
|
||||
}
|
||||
|
||||
@ -155,7 +154,10 @@ public class ThemeColorTableModel extends GDynamicColumnTableModel<ColorValue, O
|
||||
if (colorValue == null) {
|
||||
return null;
|
||||
}
|
||||
Color color = colorValue.get(valueMap);
|
||||
Color color = colorValue.hasResolvableValue(valueMap) ? colorValue.get(valueMap) : null;
|
||||
if (color == null) {
|
||||
return null;
|
||||
}
|
||||
return new ResolvedColor(id, colorValue.getReferenceId(), color);
|
||||
}
|
||||
|
||||
|
@ -49,8 +49,11 @@ public class ThemeDialog extends DialogComponentProvider {
|
||||
private ThemeFontTable fontTable;
|
||||
private ThemeIconTable iconTable;
|
||||
|
||||
public ThemeDialog() {
|
||||
private ThemeManager themeManager;
|
||||
|
||||
public ThemeDialog(ThemeManager themeManager) {
|
||||
super("Theme Dialog", false);
|
||||
this.themeManager = themeManager;
|
||||
addWorkPanel(createMainPanel());
|
||||
|
||||
addDismissButton();
|
||||
@ -79,7 +82,7 @@ public class ThemeDialog extends DialogComponentProvider {
|
||||
.enabledWhen(c -> c.isChanged())
|
||||
.popupWhen(c -> true)
|
||||
.helpLocation(new HelpLocation("Theming", "Restore_Value"))
|
||||
.onAction(c -> c.getThemeValue().installValue())
|
||||
.onAction(c -> c.getThemeValue().installValue(themeManager))
|
||||
.build();
|
||||
addAction(resetValueAction);
|
||||
}
|
||||
@ -93,44 +96,44 @@ public class ThemeDialog extends DialogComponentProvider {
|
||||
}
|
||||
|
||||
private boolean handleChanges() {
|
||||
if (Gui.hasThemeChanges()) {
|
||||
if (themeManager.hasThemeChanges()) {
|
||||
int result = OptionDialog.showYesNoCancelDialog(null, "Close Theme Dialog",
|
||||
"You have changed the theme.\n Do you want save your changes?");
|
||||
if (result == OptionDialog.CANCEL_OPTION) {
|
||||
return false;
|
||||
}
|
||||
if (result == OptionDialog.YES_OPTION) {
|
||||
return ThemeUtils.saveThemeChanges();
|
||||
return ThemeUtils.saveThemeChanges(themeManager);
|
||||
}
|
||||
Gui.restoreThemeValues();
|
||||
themeManager.restoreThemeValues();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void saveCallback() {
|
||||
ThemeUtils.saveThemeChanges();
|
||||
ThemeUtils.saveThemeChanges(themeManager);
|
||||
}
|
||||
|
||||
private void restoreCallback() {
|
||||
if (Gui.hasThemeChanges()) {
|
||||
if (themeManager.hasThemeChanges()) {
|
||||
int result = OptionDialog.showYesNoDialog(null, "Restore Theme Values",
|
||||
"Are you sure you want to discard all your changes?");
|
||||
if (result == OptionDialog.NO_OPTION) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
Gui.restoreThemeValues();
|
||||
themeManager.restoreThemeValues();
|
||||
}
|
||||
|
||||
private void reloadDefaultsCallback() {
|
||||
if (Gui.hasThemeChanges()) {
|
||||
if (themeManager.hasThemeChanges()) {
|
||||
int result = OptionDialog.showYesNoDialog(null, "Reload Ghidra Default Values",
|
||||
"This will discard all your theme changes. Continue?");
|
||||
if (result == OptionDialog.NO_OPTION) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
Gui.reloadApplicationDefaults();
|
||||
themeManager.reloadApplicationDefaults();
|
||||
}
|
||||
|
||||
private void reset() {
|
||||
@ -147,15 +150,15 @@ public class ThemeDialog extends DialogComponentProvider {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ThemeUtils.askToSaveThemeChanges()) {
|
||||
if (!ThemeUtils.askToSaveThemeChanges(themeManager)) {
|
||||
Swing.runLater(() -> updateCombo());
|
||||
return;
|
||||
}
|
||||
String themeName = (String) e.getItem();
|
||||
|
||||
Swing.runLater(() -> {
|
||||
GTheme theme = Gui.getTheme(themeName);
|
||||
Gui.setTheme(theme);
|
||||
GTheme theme = themeManager.getTheme(themeName);
|
||||
themeManager.setTheme(theme);
|
||||
if (theme.getLookAndFeelType() == LafType.GTK) {
|
||||
setStatusText(
|
||||
"Warning - Themes using the GTK LookAndFeel do not support changing java component colors, fonts or icons.",
|
||||
@ -171,7 +174,7 @@ public class ThemeDialog extends DialogComponentProvider {
|
||||
}
|
||||
|
||||
private void updateButtons() {
|
||||
boolean hasChanges = Gui.hasThemeChanges();
|
||||
boolean hasChanges = themeManager.hasThemeChanges();
|
||||
saveButton.setEnabled(hasChanges);
|
||||
restoreButton.setEnabled(hasChanges);
|
||||
}
|
||||
@ -194,25 +197,25 @@ public class ThemeDialog extends DialogComponentProvider {
|
||||
}
|
||||
|
||||
private void updateCombo() {
|
||||
Set<GTheme> supportedThemes = Gui.getSupportedThemes();
|
||||
Set<GTheme> supportedThemes = themeManager.getSupportedThemes();
|
||||
List<String> themeNames =
|
||||
supportedThemes.stream().map(t -> t.getName()).collect(Collectors.toList());
|
||||
Collections.sort(themeNames);
|
||||
combo.removeItemListener(comboListener);
|
||||
combo.setModel(new DefaultComboBoxModel<String>(new Vector<String>(themeNames)));
|
||||
combo.setSelectedItem(Gui.getActiveTheme().getName());
|
||||
combo.setSelectedItem(themeManager.getActiveTheme().getName());
|
||||
combo.addItemListener(comboListener);
|
||||
}
|
||||
|
||||
private Component buildThemeCombo() {
|
||||
JPanel panel = new JPanel();
|
||||
Set<GTheme> supportedThemes = Gui.getSupportedThemes();
|
||||
Set<GTheme> supportedThemes = themeManager.getSupportedThemes();
|
||||
List<String> themeNames =
|
||||
supportedThemes.stream().map(t -> t.getName()).collect(Collectors.toList());
|
||||
Collections.sort(themeNames);
|
||||
|
||||
combo = new GhidraComboBox<>(themeNames);
|
||||
combo.setSelectedItem(Gui.getActiveTheme().getName());
|
||||
combo.setSelectedItem(themeManager.getActiveTheme().getName());
|
||||
combo.addItemListener(comboListener);
|
||||
|
||||
panel.add(new JLabel("Theme: "), BorderLayout.WEST);
|
||||
@ -223,9 +226,9 @@ public class ThemeDialog extends DialogComponentProvider {
|
||||
|
||||
private Component buildTabedTables() {
|
||||
tabbedPane = new JTabbedPane();
|
||||
colorTable = new ThemeColorTable();
|
||||
fontTable = new ThemeFontTable();
|
||||
iconTable = new ThemeIconTable();
|
||||
colorTable = new ThemeColorTable(themeManager);
|
||||
fontTable = new ThemeFontTable(themeManager);
|
||||
iconTable = new ThemeIconTable(themeManager);
|
||||
tabbedPane.add("Colors", colorTable);
|
||||
tabbedPane.add("Fonts", fontTable);
|
||||
tabbedPane.add("Icons", iconTable);
|
||||
@ -250,12 +253,16 @@ public class ThemeDialog extends DialogComponentProvider {
|
||||
return saveButton;
|
||||
}
|
||||
|
||||
public static void editTheme() {
|
||||
/**
|
||||
* Edits the current theme
|
||||
* @param themeManager the application ThemeManager
|
||||
*/
|
||||
public static void editTheme(ThemeManager themeManager) {
|
||||
if (INSTANCE != null) {
|
||||
INSTANCE.toFront();
|
||||
return;
|
||||
}
|
||||
INSTANCE = new ThemeDialog();
|
||||
INSTANCE = new ThemeDialog(themeManager);
|
||||
DockingWindowManager.showDialog(INSTANCE);
|
||||
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ import docking.action.ActionContextProvider;
|
||||
import docking.widgets.table.GFilterTable;
|
||||
import docking.widgets.table.GTable;
|
||||
import generic.theme.FontValue;
|
||||
import generic.theme.Gui;
|
||||
import generic.theme.ThemeManager;
|
||||
import ghidra.util.Swing;
|
||||
|
||||
/**
|
||||
@ -41,11 +41,13 @@ public class ThemeFontTable extends JPanel implements ActionContextProvider {
|
||||
private FontValueEditor fontEditor = new FontValueEditor(this::fontValueChanged);
|
||||
private GTable table;
|
||||
private GFilterTable<FontValue> filterTable;
|
||||
private ThemeManager themeManager;
|
||||
|
||||
public ThemeFontTable() {
|
||||
public ThemeFontTable(ThemeManager themeManager) {
|
||||
super(new BorderLayout());
|
||||
this.themeManager = themeManager;
|
||||
|
||||
fontTableModel = new ThemeFontTableModel();
|
||||
fontTableModel = new ThemeFontTableModel(themeManager);
|
||||
filterTable = new GFilterTable<>(fontTableModel);
|
||||
table = filterTable.getTable();
|
||||
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
@ -86,7 +88,7 @@ public class ThemeFontTable extends JPanel implements ActionContextProvider {
|
||||
// run later - don't rock the boat in the middle of a listener callback
|
||||
Swing.runLater(() -> {
|
||||
FontValue newValue = (FontValue) event.getNewValue();
|
||||
Gui.setFont(newValue);
|
||||
themeManager.setFont(newValue);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -39,9 +39,11 @@ public class ThemeFontTableModel extends GDynamicColumnTableModel<FontValue, Obj
|
||||
private GThemeValueMap currentValues;
|
||||
private GThemeValueMap themeValues;
|
||||
private GThemeValueMap defaultValues;
|
||||
private ThemeManager themeManager;
|
||||
|
||||
public ThemeFontTableModel() {
|
||||
public ThemeFontTableModel(ThemeManager themeManager) {
|
||||
super(new ServiceProviderStub());
|
||||
this.themeManager = themeManager;
|
||||
load();
|
||||
}
|
||||
|
||||
@ -49,7 +51,7 @@ public class ThemeFontTableModel extends GDynamicColumnTableModel<FontValue, Obj
|
||||
* Reloads the just the current values shown in the table. Called whenever a font changes.
|
||||
*/
|
||||
public void reloadCurrent() {
|
||||
currentValues = Gui.getAllValues();
|
||||
currentValues = themeManager.getCurrentValues();
|
||||
fonts = currentValues.getFonts();
|
||||
fireTableDataChanged();
|
||||
}
|
||||
@ -64,10 +66,10 @@ public class ThemeFontTableModel extends GDynamicColumnTableModel<FontValue, Obj
|
||||
}
|
||||
|
||||
private void load() {
|
||||
currentValues = Gui.getAllValues();
|
||||
currentValues = themeManager.getCurrentValues();
|
||||
fonts = currentValues.getFonts();
|
||||
themeValues = Gui.getThemeValues();
|
||||
defaultValues = Gui.getDefaults();
|
||||
themeValues = themeManager.getThemeValues();
|
||||
defaultValues = themeManager.getDefaults();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -26,8 +26,8 @@ import docking.ActionContext;
|
||||
import docking.action.ActionContextProvider;
|
||||
import docking.widgets.table.GFilterTable;
|
||||
import docking.widgets.table.GTable;
|
||||
import generic.theme.Gui;
|
||||
import generic.theme.IconValue;
|
||||
import generic.theme.ThemeManager;
|
||||
import ghidra.util.Swing;
|
||||
|
||||
/**
|
||||
@ -39,10 +39,12 @@ public class ThemeIconTable extends JPanel implements ActionContextProvider {
|
||||
private IconValueEditor iconEditor = new IconValueEditor(this::iconValueChanged);
|
||||
private GTable table;
|
||||
private GFilterTable<IconValue> filterTable;
|
||||
private ThemeManager themeManager;
|
||||
|
||||
public ThemeIconTable() {
|
||||
public ThemeIconTable(ThemeManager themeManager) {
|
||||
super(new BorderLayout());
|
||||
iconTableModel = new ThemeIconTableModel();
|
||||
this.themeManager = themeManager;
|
||||
iconTableModel = new ThemeIconTableModel(themeManager);
|
||||
filterTable = new GFilterTable<>(iconTableModel);
|
||||
table = filterTable.getTable();
|
||||
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
@ -82,7 +84,7 @@ public class ThemeIconTable extends JPanel implements ActionContextProvider {
|
||||
// run later - don't rock the boat in the middle of a listener callback
|
||||
Swing.runLater(() -> {
|
||||
IconValue newValue = (IconValue) event.getNewValue();
|
||||
Gui.setIcon(newValue);
|
||||
themeManager.setIcon(newValue);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -39,9 +39,11 @@ public class ThemeIconTableModel extends GDynamicColumnTableModel<IconValue, Obj
|
||||
private GThemeValueMap currentValues;
|
||||
private GThemeValueMap themeValues;
|
||||
private GThemeValueMap defaultValues;
|
||||
private ThemeManager themeManager;
|
||||
|
||||
public ThemeIconTableModel() {
|
||||
public ThemeIconTableModel(ThemeManager themeManager) {
|
||||
super(new ServiceProviderStub());
|
||||
this.themeManager = themeManager;
|
||||
load();
|
||||
}
|
||||
|
||||
@ -49,7 +51,7 @@ public class ThemeIconTableModel extends GDynamicColumnTableModel<IconValue, Obj
|
||||
* Reloads the just the current values shown in the table. Called whenever an icon changes.
|
||||
*/
|
||||
public void reloadCurrent() {
|
||||
currentValues = Gui.getAllValues();
|
||||
currentValues = themeManager.getCurrentValues();
|
||||
icons = currentValues.getIcons();
|
||||
fireTableDataChanged();
|
||||
}
|
||||
@ -64,10 +66,10 @@ public class ThemeIconTableModel extends GDynamicColumnTableModel<IconValue, Obj
|
||||
}
|
||||
|
||||
private void load() {
|
||||
currentValues = Gui.getAllValues();
|
||||
currentValues = themeManager.getCurrentValues();
|
||||
icons = currentValues.getIcons();
|
||||
themeValues = Gui.getThemeValues();
|
||||
defaultValues = Gui.getDefaults();
|
||||
themeValues = themeManager.getThemeValues();
|
||||
defaultValues = themeManager.getDefaults();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -43,17 +43,17 @@ public class ThemeUtils {
|
||||
* overwrites an existing file.
|
||||
* @return true if the operation was not cancelled
|
||||
*/
|
||||
public static boolean askToSaveThemeChanges() {
|
||||
if (Gui.hasThemeChanges()) {
|
||||
public static boolean askToSaveThemeChanges(ThemeManager themeManager) {
|
||||
if (themeManager.hasThemeChanges()) {
|
||||
int result = OptionDialog.showYesNoCancelDialog(null, "Save Theme Changes?",
|
||||
"You have made changes to the theme.\n Do you want save your changes?");
|
||||
if (result == OptionDialog.CANCEL_OPTION) {
|
||||
return false;
|
||||
}
|
||||
if (result == OptionDialog.YES_OPTION) {
|
||||
return ThemeUtils.saveThemeChanges();
|
||||
return ThemeUtils.saveThemeChanges(themeManager);
|
||||
}
|
||||
Gui.reloadApplicationDefaults();
|
||||
themeManager.reloadApplicationDefaults();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -63,32 +63,33 @@ public class ThemeUtils {
|
||||
* name and asking to overwrite an existing file.
|
||||
* @return true if the operation was not cancelled
|
||||
*/
|
||||
public static boolean saveThemeChanges() {
|
||||
GTheme activeTheme = Gui.getActiveTheme();
|
||||
public static boolean saveThemeChanges(ThemeManager themeManager) {
|
||||
GTheme activeTheme = themeManager.getActiveTheme();
|
||||
String name = activeTheme.getName();
|
||||
|
||||
while (!canSaveToName(name)) {
|
||||
while (!canSaveToName(themeManager, name)) {
|
||||
name = getNameFromUser(name);
|
||||
if (name == null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return saveCurrentValues(name);
|
||||
return saveCurrentValues(themeManager, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the theme to the default, handling the case where the current theme has changes.
|
||||
*/
|
||||
public static void resetThemeToDefault() {
|
||||
if (askToSaveThemeChanges()) {
|
||||
Gui.setTheme(Gui.getDefaultTheme());
|
||||
public static void resetThemeToDefault(ThemeManager themeManager) {
|
||||
if (askToSaveThemeChanges(themeManager)) {
|
||||
themeManager.setTheme(themeManager.getDefaultTheme());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports a theme. Handles the case where there are existing changes to the current theme.
|
||||
* @param themeManager the application ThemeManager
|
||||
*/
|
||||
public static void importTheme() {
|
||||
public static void importTheme(ThemeManager themeManager) {
|
||||
GhidraFileChooser chooser = new GhidraFileChooser(null);
|
||||
chooser.setTitle("Choose Theme File");
|
||||
chooser.setApproveButtonToolTipText("Select File");
|
||||
@ -100,14 +101,14 @@ public class ThemeUtils {
|
||||
if (file == null) {
|
||||
return;
|
||||
}
|
||||
importTheme(file);
|
||||
importTheme(themeManager, file);
|
||||
}
|
||||
|
||||
static void importTheme(File themeFile) {
|
||||
if (!ThemeUtils.askToSaveThemeChanges()) {
|
||||
static void importTheme(ThemeManager themeManager, File themeFile) {
|
||||
if (!ThemeUtils.askToSaveThemeChanges(themeManager)) {
|
||||
return;
|
||||
}
|
||||
GTheme startingTheme = Gui.getActiveTheme();
|
||||
GTheme startingTheme = themeManager.getActiveTheme();
|
||||
try {
|
||||
GTheme imported = GTheme.loadTheme(themeFile);
|
||||
// by setting the theme, we can let the normal save handle all the edge cases
|
||||
@ -115,9 +116,9 @@ public class ThemeUtils {
|
||||
// Also, the imported theme may contain default values which we don't want to save. So
|
||||
// by going through the usual save mechanism, only values that differ from defaults
|
||||
// be saved.
|
||||
Gui.setTheme(imported);
|
||||
if (!ThemeUtils.saveThemeChanges()) {
|
||||
Gui.setTheme(startingTheme);
|
||||
themeManager.setTheme(imported);
|
||||
if (!ThemeUtils.saveThemeChanges(themeManager)) {
|
||||
themeManager.setTheme(startingTheme);
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
@ -130,12 +131,13 @@ public class ThemeUtils {
|
||||
/**
|
||||
* Exports a theme, prompting the user to pick an file. Also handles dealing with any
|
||||
* existing changes to the current theme.
|
||||
* @param themeManager the ThemeManager that actually does the export
|
||||
*/
|
||||
public static void exportTheme() {
|
||||
if (!ThemeUtils.askToSaveThemeChanges()) {
|
||||
public static void exportTheme(ThemeManager themeManager) {
|
||||
if (!ThemeUtils.askToSaveThemeChanges(themeManager)) {
|
||||
return;
|
||||
}
|
||||
boolean hasExternalIcons = !Gui.getActiveTheme().getExternalIconFiles().isEmpty();
|
||||
boolean hasExternalIcons = !themeManager.getActiveTheme().getExternalIconFiles().isEmpty();
|
||||
String message =
|
||||
"Export as zip file? (You are not using any external icons so the zip\n" +
|
||||
"file would only contain a single theme file.)";
|
||||
@ -151,16 +153,16 @@ public class ThemeUtils {
|
||||
}
|
||||
boolean exportAsZip = result == OptionDialog.OPTION_ONE;
|
||||
|
||||
ExportThemeDialog dialog = new ExportThemeDialog(exportAsZip);
|
||||
ExportThemeDialog dialog = new ExportThemeDialog(themeManager, exportAsZip);
|
||||
DockingWindowManager.showDialog(dialog);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prompts for and deletes a selected theme.
|
||||
*/
|
||||
public static void deleteTheme() {
|
||||
public static void deleteTheme(ThemeManager themeManager) {
|
||||
List<GTheme> savedThemes = new ArrayList<>(
|
||||
Gui.getAllThemes().stream().filter(t -> t.getFile() != null).toList());
|
||||
themeManager.getAllThemes().stream().filter(t -> t.getFile() != null).toList());
|
||||
if (savedThemes.isEmpty()) {
|
||||
Msg.showInfo(ThemeUtils.class, null, "Delete Theme", "There are no deletable themes");
|
||||
return;
|
||||
@ -171,7 +173,7 @@ public class ThemeUtils {
|
||||
if (selectedTheme == null) {
|
||||
return;
|
||||
}
|
||||
if (Gui.getActiveTheme().equals(selectedTheme)) {
|
||||
if (themeManager.getActiveTheme().equals(selectedTheme)) {
|
||||
Msg.showWarn(ThemeUtils.class, null, "Delete Failed",
|
||||
"Can't delete the current theme.");
|
||||
return;
|
||||
@ -180,7 +182,7 @@ public class ThemeUtils {
|
||||
int result = OptionDialog.showYesNoDialog(null, "Delete Theme: " + fileTheme.getName(),
|
||||
"Are you sure you want to delete theme " + fileTheme.getName());
|
||||
if (result == OptionDialog.YES_OPTION) {
|
||||
Gui.deleteTheme(fileTheme);
|
||||
themeManager.deleteTheme(fileTheme);
|
||||
}
|
||||
}
|
||||
|
||||
@ -190,8 +192,8 @@ public class ThemeUtils {
|
||||
return inputDialog.getValue();
|
||||
}
|
||||
|
||||
private static boolean canSaveToName(String name) {
|
||||
GTheme existing = Gui.getTheme(name);
|
||||
private static boolean canSaveToName(ThemeManager themeManager, String name) {
|
||||
GTheme existing = themeManager.getTheme(name);
|
||||
// if no theme exists with that name, then we are save to save it
|
||||
if (existing == null) {
|
||||
return true;
|
||||
@ -210,17 +212,17 @@ public class ThemeUtils {
|
||||
return result == OptionDialog.YES_OPTION;
|
||||
}
|
||||
|
||||
private static boolean saveCurrentValues(String themeName) {
|
||||
GTheme activeTheme = Gui.getActiveTheme();
|
||||
private static boolean saveCurrentValues(ThemeManager themeManager, String themeName) {
|
||||
GTheme activeTheme = themeManager.getActiveTheme();
|
||||
File file = getSaveFile(themeName);
|
||||
|
||||
GTheme newTheme = new GTheme(file, themeName, activeTheme.getLookAndFeelType(),
|
||||
activeTheme.useDarkDefaults());
|
||||
newTheme.load(Gui.getNonDefaultValues());
|
||||
newTheme.load(themeManager.getNonDefaultValues());
|
||||
try {
|
||||
newTheme.save();
|
||||
Gui.addTheme(newTheme);
|
||||
Gui.setTheme(newTheme);
|
||||
themeManager.addTheme(newTheme);
|
||||
themeManager.setTheme(newTheme);
|
||||
}
|
||||
catch (IOException e) {
|
||||
Msg.showError(ThemeUtils.class, null, "I/O Error",
|
||||
|
@ -24,7 +24,7 @@ import javax.swing.border.EmptyBorder;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
import javax.swing.event.ChangeListener;
|
||||
|
||||
import generic.theme.Gui;
|
||||
import ghidra.docking.util.LookAndFeelUtils;
|
||||
import resources.ResourceManager;
|
||||
|
||||
/**
|
||||
@ -123,7 +123,7 @@ public class EmptyBorderButton extends JButton {
|
||||
|
||||
// Mac OSX LNF doesn't give us rollover callbacks, so we have to add a mouse listener to
|
||||
// do the work
|
||||
if (Gui.isUsingAquaUI(getUI())) {
|
||||
if (LookAndFeelUtils.isUsingAquaUI(getUI())) {
|
||||
addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseEntered(MouseEvent e) {
|
||||
|
@ -38,7 +38,6 @@ import docking.widgets.label.GLabel;
|
||||
import docking.widgets.list.GListCellRenderer;
|
||||
import generic.theme.GColor;
|
||||
import generic.theme.GIcon;
|
||||
import generic.theme.GThemeDefaults.Colors.Palette;
|
||||
import ghidra.framework.preferences.Preferences;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.exception.AssertException;
|
||||
@ -74,6 +73,8 @@ public class GhidraFileChooser extends DialogComponentProvider implements FileFi
|
||||
static final String UP_BUTTON_NAME = "UP_BUTTON";
|
||||
private static final Color FOREROUND_COLOR = new GColor("color.fg.filechooser");
|
||||
private static final Color BACKGROUND_COLOR = new GColor("color.bg.filechooser");
|
||||
private static final Color SHORTCUT_BACKGROUND_COLOR =
|
||||
new GColor("color.bg.filechooser.shortcut");
|
||||
static final String PREFERENCES_PREFIX = "G_FILE_CHOOSER";
|
||||
private static final String WIDTH_PREFERENCE_PREFIX = PREFERENCES_PREFIX + ".WIDTH.";
|
||||
private static final String HEIGHT_PREFERENCE_PREFIX = PREFERENCES_PREFIX + ".HEIGHT.";
|
||||
@ -343,7 +344,7 @@ public class GhidraFileChooser extends DialogComponentProvider implements FileFi
|
||||
|
||||
JPanel panel = new JPanel(new BorderLayout());
|
||||
panel.setBorder(BorderFactory.createLoweredBevelBorder());
|
||||
panel.setBackground(Palette.DARK_GRAY);
|
||||
panel.setBackground(SHORTCUT_BACKGROUND_COLOR);
|
||||
panel.add(shortCutPanel, BorderLayout.NORTH);
|
||||
return panel;
|
||||
}
|
||||
|
@ -22,12 +22,14 @@ import java.text.DecimalFormat;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.SwingConstants;
|
||||
import javax.swing.table.TableCellRenderer;
|
||||
import javax.swing.table.TableModel;
|
||||
|
||||
import docking.widgets.AbstractGCellRenderer;
|
||||
import generic.theme.GColor;
|
||||
import generic.theme.Gui;
|
||||
import ghidra.docking.settings.*;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.exception.AssertException;
|
||||
@ -56,7 +58,13 @@ public class GTableCellRenderer extends AbstractGCellRenderer implements TableCe
|
||||
* Constructs a new GTableCellRenderer.
|
||||
*/
|
||||
public GTableCellRenderer() {
|
||||
|
||||
// When the Look And Feel changes, renderers are not auto updated because they
|
||||
// are not part of the component tree. So listen for a change to the Look And Feel.
|
||||
Gui.addThemeListener(e -> {
|
||||
if (e.isLookAndFeelChanged()) {
|
||||
updateUI();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -100,10 +108,7 @@ public class GTableCellRenderer extends AbstractGCellRenderer implements TableCe
|
||||
"Using a GTableCellRenderer in a non-GTable table. (Model class: " +
|
||||
table.getModel().getClass().getName() + ")");
|
||||
}
|
||||
// check if LookAndFeel has changed
|
||||
if (UIManager.getUI(this) != getUI()) {
|
||||
updateUI();
|
||||
}
|
||||
|
||||
GTable gTable = (GTable) table;
|
||||
GTableCellRenderingData data = gTable.getRenderingData(column);
|
||||
Object rowObject = null;
|
||||
|
@ -45,6 +45,7 @@ import docking.widgets.tree.internal.*;
|
||||
import docking.widgets.tree.support.*;
|
||||
import docking.widgets.tree.support.GTreeSelectionEvent.EventOrigin;
|
||||
import docking.widgets.tree.tasks.*;
|
||||
import generic.theme.*;
|
||||
import generic.timer.ExpiringSwingTimer;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
@ -55,8 +56,8 @@ import ghidra.util.worker.PriorityWorker;
|
||||
* Class for creating a JTree that supports filtering, threading, and a progress bar.
|
||||
*/
|
||||
|
||||
public class GTree extends JPanel implements BusyListener {
|
||||
|
||||
public class GTree extends JPanel implements BusyListener, ThemeListener {
|
||||
private static final Color BACKGROUND = new GColor("color.bg.tree");
|
||||
private AutoScrollTree tree;
|
||||
private GTreeModel model;
|
||||
|
||||
@ -134,6 +135,7 @@ public class GTree extends JPanel implements BusyListener {
|
||||
uniquePreferenceKey));
|
||||
|
||||
filterUpdateManager = new SwingUpdateManager(1000, 30000, () -> updateModelFilter());
|
||||
Gui.addThemeListener(this);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -146,6 +148,13 @@ public class GTree extends JPanel implements BusyListener {
|
||||
threadLocalMonitor.set(monitor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void themeChanged(ThemeEvent event) {
|
||||
if (event.isLookAndFeelChanged()) {
|
||||
model.fireNodeStructureChanged(getModelRoot());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the monitor in associated with the GTree for the calling thread. This method is
|
||||
* designed to be used by slow loading nodes that are loading <b>off the Swing thread</b>. Some
|
||||
@ -1391,6 +1400,7 @@ public class GTree extends JPanel implements BusyListener {
|
||||
|
||||
public AutoScrollTree(TreeModel model) {
|
||||
super(model);
|
||||
setBackground(BACKGROUND);
|
||||
scroller = new AutoscrollAdapter(this, 5);
|
||||
|
||||
setRowHeight(-1);// variable size rows
|
||||
|
@ -31,6 +31,8 @@ public class GTreeRenderer extends DefaultTreeCellRenderer implements GComponent
|
||||
|
||||
private static final Color VALID_DROP_TARGET_COLOR = new GColor("color.bg.tree.drag");
|
||||
private static final int DEFAULT_MIN_ICON_WIDTH = 22;
|
||||
private static final Color BACKGROUND_UNSELECTED = new GColor("color.bg.tree");
|
||||
private static final Color BACKGROUND_SELECTED = new GColor("color.bg.tree.selected");
|
||||
|
||||
private Object dropTarget;
|
||||
private boolean paintDropTarget;
|
||||
@ -41,6 +43,8 @@ public class GTreeRenderer extends DefaultTreeCellRenderer implements GComponent
|
||||
|
||||
public GTreeRenderer() {
|
||||
setHTMLRenderingEnabled(false);
|
||||
setBackgroundNonSelectionColor(BACKGROUND_UNSELECTED);
|
||||
setBackgroundSelectionColor(BACKGROUND_SELECTED);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -21,10 +21,12 @@ import java.util.Iterator;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.swing.UIDefaults;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.*;
|
||||
import javax.swing.plaf.ComponentUI;
|
||||
|
||||
import docking.framework.ApplicationInformationDisplayFactory;
|
||||
import generic.theme.LafType;
|
||||
import generic.theme.ThemeManager;
|
||||
import ghidra.framework.preferences.Preferences;
|
||||
import ghidra.util.SystemUtilities;
|
||||
|
||||
@ -81,4 +83,30 @@ public class LookAndFeelUtils {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link LafType} for the currently active {@link LookAndFeel}
|
||||
* @return the {@link LafType} for the currently active {@link LookAndFeel}
|
||||
*/
|
||||
public static LafType getLookAndFeelType() {
|
||||
return ThemeManager.getInstance().getLookAndFeelType();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given UI object is using the Aqua Look and Feel.
|
||||
* @param UI the UI to examine.
|
||||
* @return true if the UI is using Aqua
|
||||
*/
|
||||
public static boolean isUsingAquaUI(ComponentUI UI) {
|
||||
return ThemeManager.getInstance().isUsingAquaUI(UI);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if 'Nimbus' is the current Look and Feel
|
||||
* @return true if 'Nimbus' is the current Look and Feel
|
||||
*/
|
||||
public static boolean isUsingNimbusUI() {
|
||||
return ThemeManager.getInstance().isUsingNimbusUI();
|
||||
}
|
||||
|
||||
}
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 187 B After Width: | Height: | Size: 727 B |
Binary file not shown.
Before Width: | Height: | Size: 185 B After Width: | Height: | Size: 692 B |
@ -41,67 +41,69 @@ import generic.theme.builtin.NimbusTheme;
|
||||
public class ThemeUtilsTest extends AbstractDockingTest {
|
||||
|
||||
private Color testColor = Palette.RED;
|
||||
private ThemeManager themeManager;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
themeManager = ThemeManager.getInstance();
|
||||
GTheme nimbusTheme = new NimbusTheme();
|
||||
GTheme metalTheme = new MetalTheme();
|
||||
Gui.addTheme(nimbusTheme);
|
||||
Gui.addTheme(metalTheme);
|
||||
Gui.setTheme(nimbusTheme);
|
||||
themeManager.addTheme(nimbusTheme);
|
||||
themeManager.addTheme(metalTheme);
|
||||
themeManager.setTheme(nimbusTheme);
|
||||
|
||||
// get rid of any leftover imported themes from previous tests
|
||||
Set<GTheme> allThemes = Gui.getAllThemes();
|
||||
Set<GTheme> allThemes = themeManager.getAllThemes();
|
||||
for (GTheme theme : allThemes) {
|
||||
if (!(theme instanceof DiscoverableGTheme)) {
|
||||
Gui.deleteTheme(theme);
|
||||
themeManager.deleteTheme(theme);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testImportThemeNonZip() throws IOException {
|
||||
assertEquals("Nimbus Theme", Gui.getActiveTheme().getName());
|
||||
assertEquals("Nimbus Theme", themeManager.getActiveTheme().getName());
|
||||
File themeFile = createThemeFile("Bob");
|
||||
ThemeUtils.importTheme(themeFile);
|
||||
assertEquals("Bob", Gui.getActiveTheme().getName());
|
||||
ThemeUtils.importTheme(themeManager, themeFile);
|
||||
assertEquals("Bob", themeManager.getActiveTheme().getName());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testImportThemeFromZip() throws IOException {
|
||||
assertEquals("Nimbus Theme", Gui.getActiveTheme().getName());
|
||||
assertEquals("Nimbus Theme", themeManager.getActiveTheme().getName());
|
||||
File themeFile = createZipThemeFile("zippy");
|
||||
ThemeUtils.importTheme(themeFile);
|
||||
assertEquals("zippy", Gui.getActiveTheme().getName());
|
||||
ThemeUtils.importTheme(themeManager, themeFile);
|
||||
assertEquals("zippy", themeManager.getActiveTheme().getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testImportThemeWithCurrentChangesCancelled() throws IOException {
|
||||
assertEquals("Nimbus Theme", Gui.getActiveTheme().getName());
|
||||
Gui.setColor("Panel.background", testColor);
|
||||
assertTrue(Gui.hasThemeChanges());
|
||||
assertEquals("Nimbus Theme", themeManager.getActiveTheme().getName());
|
||||
themeManager.setColor("Panel.background", testColor);
|
||||
assertTrue(themeManager.hasThemeChanges());
|
||||
|
||||
File themeFile = createThemeFile("Bob");
|
||||
runSwingLater(() -> ThemeUtils.importTheme(themeFile));
|
||||
runSwingLater(() -> ThemeUtils.importTheme(themeManager, themeFile));
|
||||
OptionDialog dialog = waitForDialogComponent(OptionDialog.class);
|
||||
assertNotNull(dialog);
|
||||
assertEquals("Save Theme Changes?", dialog.getTitle());
|
||||
pressButtonByText(dialog, "Cancel");
|
||||
waitForSwing();
|
||||
assertEquals("Nimbus Theme", Gui.getActiveTheme().getName());
|
||||
assertEquals("Nimbus Theme", themeManager.getActiveTheme().getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testImportThemeWithCurrentChangesSaved() throws IOException {
|
||||
assertEquals("Nimbus Theme", Gui.getActiveTheme().getName());
|
||||
assertEquals("Nimbus Theme", themeManager.getActiveTheme().getName());
|
||||
|
||||
// make a change in the current theme, so you get asked to save
|
||||
Gui.setColor("Panel.background", testColor);
|
||||
assertTrue(Gui.hasThemeChanges());
|
||||
themeManager.setColor("Panel.background", testColor);
|
||||
assertTrue(themeManager.hasThemeChanges());
|
||||
|
||||
File themeFile = createThemeFile("Bob");
|
||||
runSwingLater(() -> ThemeUtils.importTheme(themeFile));
|
||||
runSwingLater(() -> ThemeUtils.importTheme(themeManager, themeFile));
|
||||
OptionDialog dialog = waitForDialogComponent(OptionDialog.class);
|
||||
assertNotNull(dialog);
|
||||
assertEquals("Save Theme Changes?", dialog.getTitle());
|
||||
@ -111,32 +113,32 @@ public class ThemeUtilsTest extends AbstractDockingTest {
|
||||
runSwing(() -> inputDialog.setValue("Joe"));
|
||||
pressButtonByText(inputDialog, "OK");
|
||||
waitForSwing();
|
||||
assertEquals("Bob", Gui.getActiveTheme().getName());
|
||||
assertNotNull(Gui.getTheme("Joe"));
|
||||
assertEquals("Bob", themeManager.getActiveTheme().getName());
|
||||
assertNotNull(themeManager.getTheme("Joe"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testImportThemeWithCurrentChangesThrownAway() throws IOException {
|
||||
assertEquals("Nimbus Theme", Gui.getActiveTheme().getName());
|
||||
assertEquals("Nimbus Theme", themeManager.getActiveTheme().getName());
|
||||
|
||||
// make a change in the current theme, so you get asked to save
|
||||
Gui.setColor("Panel.background", testColor);
|
||||
assertTrue(Gui.hasThemeChanges());
|
||||
themeManager.setColor("Panel.background", testColor);
|
||||
assertTrue(themeManager.hasThemeChanges());
|
||||
|
||||
File bobThemeFile = createThemeFile("Bob");
|
||||
runSwingLater(() -> ThemeUtils.importTheme(bobThemeFile));
|
||||
runSwingLater(() -> ThemeUtils.importTheme(themeManager, bobThemeFile));
|
||||
|
||||
OptionDialog dialog = waitForDialogComponent(OptionDialog.class);
|
||||
assertNotNull(dialog);
|
||||
assertEquals("Save Theme Changes?", dialog.getTitle());
|
||||
pressButtonByText(dialog, "No");
|
||||
waitForSwing();
|
||||
assertEquals("Bob", Gui.getActiveTheme().getName());
|
||||
assertEquals("Bob", themeManager.getActiveTheme().getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExportThemeAsZip() throws IOException {
|
||||
runSwingLater(() -> ThemeUtils.exportTheme());
|
||||
runSwingLater(() -> ThemeUtils.exportTheme(themeManager));
|
||||
OptionDialog dialog = waitForDialogComponent(OptionDialog.class);
|
||||
pressButtonByText(dialog, "Export Zip");
|
||||
ExportThemeDialog exportDialog = waitForDialogComponent(ExportThemeDialog.class);
|
||||
@ -151,7 +153,7 @@ public class ThemeUtilsTest extends AbstractDockingTest {
|
||||
|
||||
@Test
|
||||
public void testExportThemeAsFile() throws IOException {
|
||||
runSwingLater(() -> ThemeUtils.exportTheme());
|
||||
runSwingLater(() -> ThemeUtils.exportTheme(themeManager));
|
||||
OptionDialog dialog = waitForDialogComponent(OptionDialog.class);
|
||||
pressButtonByText(dialog, "Export File");
|
||||
ExportThemeDialog exportDialog = waitForDialogComponent(ExportThemeDialog.class);
|
||||
@ -167,29 +169,29 @@ public class ThemeUtilsTest extends AbstractDockingTest {
|
||||
@Test
|
||||
public void testDeleteTheme() throws IOException {
|
||||
File themeFile = createThemeFile("Bob");
|
||||
ThemeUtils.importTheme(themeFile);
|
||||
ThemeUtils.importTheme(themeManager, themeFile);
|
||||
themeFile = createThemeFile("Joe");
|
||||
ThemeUtils.importTheme(themeFile);
|
||||
ThemeUtils.importTheme(themeManager, themeFile);
|
||||
themeFile = createThemeFile("Lisa");
|
||||
ThemeUtils.importTheme(themeFile);
|
||||
ThemeUtils.importTheme(themeManager, themeFile);
|
||||
|
||||
assertNotNull(Gui.getTheme("Bob"));
|
||||
assertNotNull(Gui.getTheme("Joe"));
|
||||
assertNotNull(Gui.getTheme("Lisa"));
|
||||
assertNotNull(themeManager.getTheme("Bob"));
|
||||
assertNotNull(themeManager.getTheme("Joe"));
|
||||
assertNotNull(themeManager.getTheme("Lisa"));
|
||||
|
||||
runSwingLater(() -> ThemeUtils.deleteTheme());
|
||||
runSwingLater(() -> ThemeUtils.deleteTheme(themeManager));
|
||||
@SuppressWarnings("unchecked")
|
||||
SelectFromListDialog<GTheme> dialog = waitForDialogComponent(SelectFromListDialog.class);
|
||||
runSwing(() -> dialog.setSelectedObject(Gui.getTheme("Bob")));
|
||||
runSwing(() -> dialog.setSelectedObject(themeManager.getTheme("Bob")));
|
||||
pressButtonByText(dialog, "OK");
|
||||
|
||||
OptionDialog optionDialog = waitForDialogComponent(OptionDialog.class);
|
||||
pressButtonByText(optionDialog, "Yes");
|
||||
waitForSwing();
|
||||
|
||||
assertNotNull(Gui.getTheme("Bob"));
|
||||
assertNull(Gui.getTheme("Joe"));
|
||||
assertNotNull(Gui.getTheme("Lisa"));
|
||||
assertNotNull(themeManager.getTheme("Bob"));
|
||||
assertNull(themeManager.getTheme("Joe"));
|
||||
assertNotNull(themeManager.getTheme("Lisa"));
|
||||
|
||||
}
|
||||
|
||||
@ -231,7 +233,7 @@ public class ThemeUtilsTest extends AbstractDockingTest {
|
||||
File file = createTempFile("Test_Theme", ".theme.zip");
|
||||
GTheme outputTheme = new GTheme(file, themeName, LafType.METAL, false);
|
||||
outputTheme.addColor(new ColorValue("Panel.Background", testColor));
|
||||
outputTheme.saveToZip(file, false);
|
||||
new ThemeWriter(outputTheme).writeThemeToZipFile(file);
|
||||
return file;
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,7 @@ icon.expand.all = expand_all.png
|
||||
|
||||
icon.configure.filter = exec.png
|
||||
icon.clear = erase16.png
|
||||
icon.delete = icon.error
|
||||
icon.delete = edit-delete.png
|
||||
icon.error = emblem-important.png
|
||||
|
||||
icon.home = go-home.png
|
||||
|
@ -0,0 +1,484 @@
|
||||
/* ###
|
||||
* 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 generic.theme;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.plaf.ComponentUI;
|
||||
|
||||
import com.formdev.flatlaf.FlatDarkLaf;
|
||||
import com.formdev.flatlaf.FlatLightLaf;
|
||||
|
||||
import generic.theme.laf.LookAndFeelManager;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.classfinder.ClassSearcher;
|
||||
|
||||
/**
|
||||
* This is the fully functional {@link ThemeManager} that manages themes in a application. To
|
||||
* activate the theme functionality, Applications (or tests) must call
|
||||
* {@link ApplicationThemeManager#initialize()}
|
||||
*/
|
||||
public class ApplicationThemeManager extends ThemeManager {
|
||||
private GTheme activeTheme = getDefaultTheme();
|
||||
private Set<GTheme> allThemes = null;
|
||||
|
||||
private GThemeValueMap applicationDefaults = new GThemeValueMap();
|
||||
private GThemeValueMap applicationDarkDefaults = new GThemeValueMap();
|
||||
private GThemeValueMap javaDefaults = new GThemeValueMap();
|
||||
private GThemeValueMap systemValues = new GThemeValueMap();
|
||||
|
||||
protected ThemeFileLoader themeFileLoader = new ThemeFileLoader();
|
||||
protected ThemePreferences themePreferences = new ThemePreferences();
|
||||
|
||||
private Map<String, GColorUIResource> gColorMap = new HashMap<>();
|
||||
private Map<String, GIconUIResource> gIconMap = new HashMap<>();
|
||||
|
||||
// stores the original value for ids whose value has changed from the current theme
|
||||
private GThemeValueMap changedValuesMap = new GThemeValueMap();
|
||||
protected LookAndFeelManager lookAndFeelManager;
|
||||
|
||||
/**
|
||||
* Initialized the Theme and its values for the application.
|
||||
*/
|
||||
public static void initialize() {
|
||||
if (INSTANCE instanceof ApplicationThemeManager) {
|
||||
Msg.error(ThemeManager.class, "Attempted to initialize theming more than once!");
|
||||
return;
|
||||
}
|
||||
|
||||
ApplicationThemeManager themeManager = new ApplicationThemeManager();
|
||||
themeManager.doInitialize();
|
||||
}
|
||||
|
||||
protected ApplicationThemeManager() {
|
||||
// AppliationThemeManagers always replace any other instances
|
||||
INSTANCE = this;
|
||||
installInGui();
|
||||
}
|
||||
|
||||
protected void doInitialize() {
|
||||
installFlatLookAndFeels();
|
||||
loadThemeDefaults();
|
||||
setTheme(themePreferences.load());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reloadApplicationDefaults() {
|
||||
loadThemeDefaults();
|
||||
buildCurrentValues();
|
||||
lookAndFeelManager.resetAll(javaDefaults);
|
||||
notifyThemeChanged(new AllValuesChangedThemeEvent(false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreThemeValues() {
|
||||
buildCurrentValues();
|
||||
lookAndFeelManager.resetAll(javaDefaults);
|
||||
notifyThemeChanged(new AllValuesChangedThemeEvent(false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreColor(String id) {
|
||||
if (changedValuesMap.containsColor(id)) {
|
||||
setColor(changedValuesMap.getColor(id));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreFont(String id) {
|
||||
if (changedValuesMap.containsFont(id)) {
|
||||
setFont(changedValuesMap.getFont(id));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreIcon(String id) {
|
||||
if (changedValuesMap.containsIcon(id)) {
|
||||
setIcon(changedValuesMap.getIcon(id));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChangedColor(String id) {
|
||||
return changedValuesMap.containsColor(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChangedFont(String id) {
|
||||
return changedValuesMap.containsFont(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChangedIcon(String id) {
|
||||
return changedValuesMap.containsIcon(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTheme(GTheme theme) {
|
||||
if (theme.hasSupportedLookAndFeel()) {
|
||||
activeTheme = theme;
|
||||
LafType lafType = theme.getLookAndFeelType();
|
||||
lookAndFeelManager = lafType.getLookAndFeelManager(this);
|
||||
try {
|
||||
lookAndFeelManager.installLookAndFeel();
|
||||
themePreferences.save(theme);
|
||||
notifyThemeChanged(new AllValuesChangedThemeEvent(true));
|
||||
}
|
||||
catch (Exception e) {
|
||||
Msg.error(this, "Error setting LookAndFeel: " + lafType.getName(), e);
|
||||
}
|
||||
}
|
||||
currentValues.checkForUnresolvedReferences();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTheme(GTheme newTheme) {
|
||||
loadThemes();
|
||||
allThemes.remove(newTheme);
|
||||
allThemes.add(newTheme);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteTheme(GTheme theme) {
|
||||
File file = theme.getFile();
|
||||
if (file != null) {
|
||||
file.delete();
|
||||
}
|
||||
if (allThemes != null) {
|
||||
allThemes.remove(theme);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<GTheme> getAllThemes() {
|
||||
loadThemes();
|
||||
return new HashSet<>(allThemes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<GTheme> getSupportedThemes() {
|
||||
loadThemes();
|
||||
Set<GTheme> supported = new HashSet<>();
|
||||
for (GTheme theme : allThemes) {
|
||||
if (theme.hasSupportedLookAndFeel()) {
|
||||
supported.add(theme);
|
||||
}
|
||||
}
|
||||
return supported;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GTheme getActiveTheme() {
|
||||
return activeTheme;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LafType getLookAndFeelType() {
|
||||
return activeTheme.getLookAndFeelType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public GTheme getTheme(String themeName) {
|
||||
Optional<GTheme> first =
|
||||
getAllThemes().stream().filter(t -> t.getName().equals(themeName)).findFirst();
|
||||
return first.orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GThemeValueMap getThemeValues() {
|
||||
GThemeValueMap map = new GThemeValueMap();
|
||||
map.load(javaDefaults);
|
||||
map.load(systemValues);
|
||||
map.load(applicationDefaults);
|
||||
if (activeTheme.useDarkDefaults()) {
|
||||
map.load(applicationDarkDefaults);
|
||||
}
|
||||
map.load(activeTheme);
|
||||
return map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFont(FontValue newValue) {
|
||||
FontValue currentValue = currentValues.getFont(newValue.getId());
|
||||
if (newValue.equals(currentValue)) {
|
||||
return;
|
||||
}
|
||||
updateChangedValuesMap(currentValue, newValue);
|
||||
|
||||
currentValues.addFont(newValue);
|
||||
notifyThemeChanged(new FontChangedThemeEvent(currentValues, newValue));
|
||||
|
||||
// update all java LookAndFeel fonts affected by this changed
|
||||
String id = newValue.getId();
|
||||
Set<String> changedFontIds = findChangedJavaFontIds(id);
|
||||
lookAndFeelManager.fontsChanged(changedFontIds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setColor(ColorValue newValue) {
|
||||
ColorValue currentValue = currentValues.getColor(newValue.getId());
|
||||
if (newValue.equals(currentValue)) {
|
||||
return;
|
||||
}
|
||||
updateChangedValuesMap(currentValue, newValue);
|
||||
currentValues.addColor(newValue);
|
||||
notifyThemeChanged(new ColorChangedThemeEvent(currentValues, newValue));
|
||||
|
||||
// now update the ui
|
||||
if (lookAndFeelManager != null) {
|
||||
lookAndFeelManager.colorsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIcon(IconValue newValue) {
|
||||
IconValue currentValue = currentValues.getIcon(newValue.getId());
|
||||
if (newValue.equals(currentValue)) {
|
||||
return;
|
||||
}
|
||||
updateChangedValuesMap(currentValue, newValue);
|
||||
|
||||
currentValues.addIcon(newValue);
|
||||
notifyThemeChanged(new IconChangedThemeEvent(currentValues, newValue));
|
||||
|
||||
// now update the ui
|
||||
// update all java LookAndFeel icons affected by this changed
|
||||
String id = newValue.getId();
|
||||
Set<String> changedIconIds = findChangedJavaIconIds(id);
|
||||
Icon newIcon = newValue.get(currentValues);
|
||||
lookAndFeelManager.iconsChanged(changedIconIds, newIcon);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GColorUIResource getGColorUiResource(String id) {
|
||||
GColorUIResource gColor = gColorMap.get(id);
|
||||
if (gColor == null) {
|
||||
gColor = new GColorUIResource(id);
|
||||
gColorMap.put(id, gColor);
|
||||
}
|
||||
return gColor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GIconUIResource getGIconUiResource(String id) {
|
||||
|
||||
GIconUIResource gIcon = gIconMap.get(id);
|
||||
if (gIcon == null) {
|
||||
gIcon = new GIconUIResource(id);
|
||||
gIconMap.put(id, gIcon);
|
||||
}
|
||||
return gIcon;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GThemeValueMap getJavaDefaults() {
|
||||
GThemeValueMap map = new GThemeValueMap();
|
||||
map.load(javaDefaults);
|
||||
return map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GThemeValueMap getApplicationDarkDefaults() {
|
||||
GThemeValueMap map = new GThemeValueMap(applicationDefaults);
|
||||
map.load(applicationDarkDefaults);
|
||||
return map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GThemeValueMap getApplicationLightDefaults() {
|
||||
GThemeValueMap map = new GThemeValueMap(applicationDefaults);
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link GThemeValueMap} containing all default values for the current theme. It
|
||||
* is a combination of application defined defaults and java {@link LookAndFeel} defaults.
|
||||
* @return the current set of defaults.
|
||||
*/
|
||||
public GThemeValueMap getDefaults() {
|
||||
GThemeValueMap currentDefaults = new GThemeValueMap(javaDefaults);
|
||||
currentDefaults.load(systemValues);
|
||||
currentDefaults.load(applicationDefaults);
|
||||
if (activeTheme.useDarkDefaults()) {
|
||||
currentDefaults.load(applicationDarkDefaults);
|
||||
}
|
||||
return currentDefaults;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets specially defined system UI values. These values are created by the application as a
|
||||
* convenience for mapping generic concepts to values that differ by Look and Feel. This allows
|
||||
* clients to use 'system' properties without knowing the actual Look and Feel terms.
|
||||
*
|
||||
* <p>For example, 'system.color.border' defaults to 'controlShadow', but maps to 'nimbusBorder'
|
||||
* in the Nimbus Look and Feel.
|
||||
*
|
||||
* @param map the map
|
||||
*/
|
||||
public void setSystemDefaults(GThemeValueMap map) {
|
||||
systemValues = map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the map of Java default UI values. These are the UI values defined by the current Java
|
||||
* Look and Feel.
|
||||
* @param map the default theme values defined by the {@link LookAndFeel}
|
||||
*/
|
||||
public void setJavaDefaults(GThemeValueMap map) {
|
||||
javaDefaults = map;
|
||||
buildCurrentValues();
|
||||
GColor.refreshAll(currentValues);
|
||||
GIcon.refreshAll(currentValues);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUsingAquaUI(ComponentUI UI) {
|
||||
return activeTheme.getLookAndFeelType() == LafType.MAC;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUsingNimbusUI() {
|
||||
return activeTheme.getLookAndFeelType() == LafType.NIMBUS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasThemeChanges() {
|
||||
return !changedValuesMap.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerFont(Component component, String fontId) {
|
||||
lookAndFeelManager.registerFont(component, fontId);
|
||||
}
|
||||
|
||||
public boolean isDarkTheme() {
|
||||
return activeTheme.useDarkDefaults();
|
||||
}
|
||||
|
||||
private void installFlatLookAndFeels() {
|
||||
UIManager.installLookAndFeel(LafType.FLAT_LIGHT.getName(), FlatLightLaf.class.getName());
|
||||
UIManager.installLookAndFeel(LafType.FLAT_DARK.getName(), FlatDarkLaf.class.getName());
|
||||
}
|
||||
|
||||
private void loadThemeDefaults() {
|
||||
themeFileLoader.loadThemeDefaultFiles();
|
||||
applicationDefaults = themeFileLoader.getDefaults();
|
||||
applicationDarkDefaults = themeFileLoader.getDarkDefaults();
|
||||
}
|
||||
|
||||
private void buildCurrentValues() {
|
||||
GThemeValueMap map = new GThemeValueMap();
|
||||
|
||||
map.load(javaDefaults);
|
||||
map.load(systemValues);
|
||||
map.load(applicationDefaults);
|
||||
if (activeTheme.useDarkDefaults()) {
|
||||
map.load(applicationDarkDefaults);
|
||||
}
|
||||
map.load(activeTheme);
|
||||
currentValues = map;
|
||||
changedValuesMap.clear();
|
||||
}
|
||||
|
||||
private void loadThemes() {
|
||||
if (allThemes == null) {
|
||||
Set<GTheme> set = new HashSet<>();
|
||||
set.addAll(findDiscoverableThemes());
|
||||
set.addAll(themeFileLoader.loadThemeFiles());
|
||||
allThemes = set;
|
||||
}
|
||||
}
|
||||
|
||||
private Collection<DiscoverableGTheme> findDiscoverableThemes() {
|
||||
return ClassSearcher.getInstances(DiscoverableGTheme.class);
|
||||
}
|
||||
|
||||
private void updateChangedValuesMap(ColorValue currentValue, ColorValue newValue) {
|
||||
String id = newValue.getId();
|
||||
ColorValue originalValue = changedValuesMap.getColor(id);
|
||||
|
||||
// if new value is original value, it is no longer changed, remove it from changed map
|
||||
if (newValue.equals(originalValue)) {
|
||||
changedValuesMap.removeColor(id);
|
||||
}
|
||||
else if (originalValue == null) {
|
||||
// first time changed, so current value is original value
|
||||
changedValuesMap.addColor(currentValue);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateChangedValuesMap(FontValue currentValue, FontValue newValue) {
|
||||
String id = newValue.getId();
|
||||
FontValue originalValue = changedValuesMap.getFont(id);
|
||||
|
||||
// if new value is original value, it is no longer changed, remove it from changed map
|
||||
if (newValue.equals(originalValue)) {
|
||||
changedValuesMap.removeFont(id);
|
||||
}
|
||||
else if (originalValue == null) {
|
||||
// first time changed, so current value is original value
|
||||
changedValuesMap.addFont(currentValue);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateChangedValuesMap(IconValue currentValue, IconValue newValue) {
|
||||
String id = newValue.getId();
|
||||
IconValue originalValue = changedValuesMap.getIcon(id);
|
||||
|
||||
// if new value is original value, it is no longer changed, remove it from changed map
|
||||
if (newValue.equals(originalValue)) {
|
||||
changedValuesMap.removeIcon(id);
|
||||
}
|
||||
else if (originalValue == null) {
|
||||
// first time changed, so current value is original value
|
||||
changedValuesMap.addIcon(currentValue);
|
||||
}
|
||||
}
|
||||
|
||||
private Set<String> findChangedJavaFontIds(String id) {
|
||||
Set<String> affectedIds = new HashSet<>();
|
||||
List<FontValue> fonts = javaDefaults.getFonts();
|
||||
for (FontValue fontValue : fonts) {
|
||||
String fontId = fontValue.getId();
|
||||
FontValue currentFontValue = currentValues.getFont(fontId);
|
||||
if (fontId.equals(id) || currentFontValue.inheritsFrom(id, currentValues)) {
|
||||
affectedIds.add(fontId);
|
||||
}
|
||||
}
|
||||
return affectedIds;
|
||||
}
|
||||
|
||||
private Set<String> findChangedJavaIconIds(String id) {
|
||||
Set<String> affectedIds = new HashSet<>();
|
||||
List<IconValue> icons = javaDefaults.getIcons();
|
||||
for (IconValue iconValue : icons) {
|
||||
String iconId = iconValue.getId();
|
||||
if (iconId.equals(id) || iconValue.inheritsFrom(id, currentValues)) {
|
||||
affectedIds.add(iconId);
|
||||
}
|
||||
}
|
||||
return affectedIds;
|
||||
}
|
||||
|
||||
public void refreshGThemeValues() {
|
||||
GColor.refreshAll(currentValues);
|
||||
GIcon.refreshAll(currentValues);
|
||||
}
|
||||
|
||||
}
|
@ -150,8 +150,8 @@ public class ColorValue extends ThemeValue<Color> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void installValue() {
|
||||
Gui.setColor(this);
|
||||
public void installValue(ThemeManager themeManager) {
|
||||
themeManager.setColor(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -232,8 +232,8 @@ public class FontValue extends ThemeValue<Font> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void installValue() {
|
||||
Gui.setFont(this);
|
||||
public void installValue(ThemeManager themeManager) {
|
||||
themeManager.setFont(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -50,20 +50,9 @@ public class GColor extends Color {
|
||||
* @param id the id used to lookup the current value for this color
|
||||
*/
|
||||
public GColor(String id) {
|
||||
this(id, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a GColor with an id that will be used to look up the current color associated with
|
||||
* that id, which can be changed at runtime.
|
||||
* @param id the id used to lookup the current value for this color
|
||||
* @param validate if true, an error will be generated if the id can't be resolved to a color
|
||||
* at this time
|
||||
*/
|
||||
public GColor(String id, boolean validate) {
|
||||
super(0x808080);
|
||||
this.id = id;
|
||||
delegate = Gui.getColor(id, validate);
|
||||
delegate = Gui.getColor(id);
|
||||
inUseColors.add(this);
|
||||
|
||||
}
|
||||
@ -230,9 +219,11 @@ public class GColor extends Color {
|
||||
|
||||
/**
|
||||
* Reloads the delegate.
|
||||
* @param currentValues the map of current theme values
|
||||
*/
|
||||
public void refresh() {
|
||||
Color color = Gui.getColor(id, false);
|
||||
public void refresh(GThemeValueMap currentValues) {
|
||||
ColorValue value = currentValues.getColor(id);
|
||||
Color color = value == null ? null : value.get(currentValues);
|
||||
if (color != null) {
|
||||
if (alpha != null) {
|
||||
delegate = new Color(color.getRed(), color.getGreen(), color.getBlue(), alpha);
|
||||
@ -246,10 +237,11 @@ public class GColor extends Color {
|
||||
/**
|
||||
* Static method for notifying all the existing GColors that colors have changed and they
|
||||
* should reload their cached indirect color.
|
||||
* @param currentValues the map of current theme values
|
||||
*/
|
||||
public static void refreshAll() {
|
||||
public static void refreshAll(GThemeValueMap currentValues) {
|
||||
for (GColor gcolor : inUseColors.getValues()) {
|
||||
gcolor.refresh();
|
||||
gcolor.refresh(currentValues);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -46,10 +46,11 @@ public class GIcon implements Icon {
|
||||
/**
|
||||
* Static method for notifying all the existing GIcon that icons have changed and they
|
||||
* should reload their cached indirect icon.
|
||||
* @param currentValues the map of all current theme values
|
||||
*/
|
||||
public static void refreshAll() {
|
||||
public static void refreshAll(GThemeValueMap currentValues) {
|
||||
for (GIcon gIcon : inUseIcons.getValues()) {
|
||||
gIcon.refresh();
|
||||
gIcon.refresh(currentValues);
|
||||
}
|
||||
}
|
||||
|
||||
@ -59,19 +60,8 @@ public class GIcon implements Icon {
|
||||
* @param id the id used to lookup the current value for this color
|
||||
*/
|
||||
public GIcon(String id) {
|
||||
this(id, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a GIcon with an id that will be used to look up the current icon associated with
|
||||
* that id, which can be changed at runtime.
|
||||
* @param id the id used to lookup the current value for this icon
|
||||
* @param validate if true, an error will be generated if the id can't be resolved to a icon
|
||||
* at this time
|
||||
*/
|
||||
public GIcon(String id, boolean validate) {
|
||||
this.id = id;
|
||||
delegate = Gui.getIcon(id, validate);
|
||||
delegate = Gui.getIcon(id);
|
||||
inUseIcons.add(this);
|
||||
}
|
||||
|
||||
@ -134,14 +124,25 @@ public class GIcon implements Icon {
|
||||
|
||||
/**
|
||||
* Reloads the delegate.
|
||||
* @param currentValues the map of current theme values
|
||||
*/
|
||||
public void refresh() {
|
||||
Icon icon = Gui.getIcon(id, false);
|
||||
public void refresh(GThemeValueMap currentValues) {
|
||||
IconValue value = currentValues.getIcon(id);
|
||||
Icon icon = value == null ? null : value.get(currentValues);
|
||||
if (icon != null) {
|
||||
delegate = icon;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current delegate for this GIcon. Note that this delegate can change when the
|
||||
* theme changes or is edited.
|
||||
* @return the current delegate icon for this GIcon.
|
||||
*/
|
||||
public Icon getDelegate() {
|
||||
return delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return id.hashCode();
|
||||
|
@ -217,43 +217,6 @@ public class GTheme extends GThemeValueMap {
|
||||
writer.writeThemeToFile(file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves this theme to a new theme file.
|
||||
* @param outputFile the file to save to
|
||||
* @param includeDefaults if true, write all values to the theme file including default values.
|
||||
* Otherwise, just values that are not the default values are written to the file.
|
||||
* @return a new FileGTheme that represents the new file/theme
|
||||
* @throws IOException if an I/O error occurs writing the theme file
|
||||
*/
|
||||
public GTheme saveToFile(File outputFile, boolean includeDefaults) throws IOException {
|
||||
|
||||
GTheme fileTheme = new GTheme(outputFile, name, lookAndFeel, useDarkDefaults);
|
||||
if (includeDefaults) {
|
||||
fileTheme.load(Gui.getDefaults());
|
||||
}
|
||||
fileTheme.load(this);
|
||||
fileTheme.save();
|
||||
return fileTheme;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves this theme to a new theme file.
|
||||
* @param outputFile the file to save to
|
||||
* @param includeDefaults if true, write all values to the theme file including default values.
|
||||
* Otherwise, just values that are not the default values are written to the file.
|
||||
* @throws IOException if an I/O error occurs writing the theme file
|
||||
*/
|
||||
public void saveToZip(File outputFile, boolean includeDefaults) throws IOException {
|
||||
|
||||
GTheme theme = new GTheme(name, lookAndFeel, useDarkDefaults);
|
||||
if (includeDefaults) {
|
||||
theme.load(Gui.getDefaults());
|
||||
}
|
||||
theme.load(this);
|
||||
ThemeWriter writer = new ThemeWriter(theme);
|
||||
writer.writeThemeToZipFile(outputFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a theme from a file. The file can be either a theme file or a zip file containing
|
||||
* a theme file and optionally a set of icon files.
|
||||
|
@ -16,26 +16,9 @@
|
||||
package generic.theme;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.plaf.ComponentUI;
|
||||
|
||||
import com.formdev.flatlaf.FlatDarkLaf;
|
||||
import com.formdev.flatlaf.FlatLightLaf;
|
||||
|
||||
import generic.theme.builtin.*;
|
||||
import generic.theme.laf.LookAndFeelManager;
|
||||
import ghidra.framework.OperatingSystem;
|
||||
import ghidra.framework.Platform;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.classfinder.ClassSearcher;
|
||||
import ghidra.util.datastruct.WeakDataStructureFactory;
|
||||
import ghidra.util.datastruct.WeakSet;
|
||||
import resources.ResourceManager;
|
||||
import utilities.util.reflection.ReflectionUtilities;
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.LookAndFeel;
|
||||
|
||||
/**
|
||||
* Provides a static set of methods for globally managing application themes and their values.
|
||||
@ -53,267 +36,15 @@ import utilities.util.reflection.ReflectionUtilities;
|
||||
*
|
||||
*/
|
||||
public class Gui {
|
||||
public static final String BACKGROUND_KEY = "color.bg.text";
|
||||
|
||||
private static GTheme activeTheme = getDefaultTheme();
|
||||
private static Set<GTheme> allThemes = null;
|
||||
|
||||
private static GThemeValueMap applicationDefaults = new GThemeValueMap();
|
||||
private static GThemeValueMap applicationDarkDefaults = new GThemeValueMap();
|
||||
private static GThemeValueMap javaDefaults = new GThemeValueMap();
|
||||
private static GThemeValueMap currentValues = new GThemeValueMap();
|
||||
private static GThemeValueMap systemValues = new GThemeValueMap();
|
||||
|
||||
private static ThemeFileLoader themeFileLoader = new ThemeFileLoader();
|
||||
private static ThemePreferenceManager themePreferenceManager = new ThemePreferenceManager();
|
||||
|
||||
private static Map<String, GColorUIResource> gColorMap = new HashMap<>();
|
||||
private static boolean isInitialized;
|
||||
private static Map<String, GIconUIResource> gIconMap = new HashMap<>();
|
||||
|
||||
// these notifications are only when the user is manipulating theme values, so rare and at
|
||||
// user speed, so using copy on read
|
||||
private static WeakSet<ThemeListener> themeListeners =
|
||||
WeakDataStructureFactory.createCopyOnReadWeakSet();
|
||||
|
||||
// stores the original value for ids whose value has changed from the current theme
|
||||
private static GThemeValueMap changedValuesMap = new GThemeValueMap();
|
||||
private static LookAndFeelManager lookAndFeelManager;
|
||||
static Font DEFAULT_FONT = new Font("Dialog", Font.PLAIN, 12);
|
||||
// Start with an StubThemeManager so that simple tests can operate without having
|
||||
// to initialize the theme system. Applications and integration tests will
|
||||
// called ThemeManager.initialize() which will replace this with a fully initialized version.
|
||||
private static ThemeManager themeManager = new StubThemeManager();
|
||||
|
||||
private Gui() {
|
||||
// static utils class, can't construct
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialized the Theme and its values for the application.
|
||||
*/
|
||||
public static void initialize() {
|
||||
isInitialized = true;
|
||||
installFlatLookAndFeels();
|
||||
loadThemeDefaults();
|
||||
setTheme(themePreferenceManager.getTheme());
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads the defaults from all the discoverable theme.property files.
|
||||
*/
|
||||
public static void reloadApplicationDefaults() {
|
||||
loadThemeDefaults();
|
||||
buildCurrentValues();
|
||||
lookAndFeelManager.resetAll(javaDefaults);
|
||||
notifyThemeChanged(new AllValuesChangedThemeEvent(false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores all the current application back to the values as specified by the active theme.
|
||||
* In other words, reverts any changes to the active theme that haven't been saved.
|
||||
*/
|
||||
public static void restoreThemeValues() {
|
||||
buildCurrentValues();
|
||||
lookAndFeelManager.resetAll(javaDefaults);
|
||||
notifyThemeChanged(new AllValuesChangedThemeEvent(false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores the current color value for the given color id to the value established by the
|
||||
* current theme.
|
||||
* @param id the color id to restore back to the original theme value
|
||||
*/
|
||||
public static void restoreColor(String id) {
|
||||
if (changedValuesMap.containsColor(id)) {
|
||||
Gui.setColor(changedValuesMap.getColor(id));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores the current font value for the given font id to the value established by the
|
||||
* current theme.
|
||||
* @param id the font id to restore back to the original theme value
|
||||
*/
|
||||
public static void restoreFont(String id) {
|
||||
if (changedValuesMap.containsFont(id)) {
|
||||
Gui.setFont(changedValuesMap.getFont(id));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores the current icon value for the given icon id to the value established by the
|
||||
* current theme.
|
||||
* @param id the icon id to restore back to the original theme value
|
||||
*/
|
||||
public static void restoreIcon(String id) {
|
||||
if (changedValuesMap.containsIcon(id)) {
|
||||
Gui.setIcon(changedValuesMap.getIcon(id));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the color associated with the given id has been changed from the current
|
||||
* theme value for that id.
|
||||
* @param id the color id to check if it has been changed
|
||||
* @return true if the color associated with the given id has been changed from the current
|
||||
* theme value for that id.
|
||||
*/
|
||||
public static boolean isChangedColor(String id) {
|
||||
return changedValuesMap.containsColor(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the font associated with the given id has been changed from the current
|
||||
* theme value for that id.
|
||||
* @param id the font id to check if it has been changed
|
||||
* @return true if the font associated with the given id has been changed from the current
|
||||
* theme value for that id.
|
||||
*/
|
||||
public static boolean isChangedFont(String id) {
|
||||
return changedValuesMap.containsFont(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the Icon associated with the given id has been changed from the current
|
||||
* theme value for that id.
|
||||
* @param id the Icon id to check if it has been changed
|
||||
* @return true if the Icon associated with the given id has been changed from the current
|
||||
* theme value for that id.
|
||||
*/
|
||||
public static boolean isChangedIcon(String id) {
|
||||
return changedValuesMap.containsIcon(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the application's active theme to the given theme.
|
||||
* @param theme the theme to make active
|
||||
*/
|
||||
public static void setTheme(GTheme theme) {
|
||||
if (theme.hasSupportedLookAndFeel()) {
|
||||
activeTheme = theme;
|
||||
LafType lookAndFeel = theme.getLookAndFeelType();
|
||||
lookAndFeelManager = lookAndFeel.getLookAndFeelManager();
|
||||
try {
|
||||
lookAndFeelManager.installLookAndFeel();
|
||||
themePreferenceManager.saveThemeToPreferences(theme);
|
||||
notifyThemeChanged(new AllValuesChangedThemeEvent(true));
|
||||
}
|
||||
catch (Exception e) {
|
||||
Msg.error(Gui.class,
|
||||
"Error setting LookAndFeel: " + lookAndFeel.getName(), e);
|
||||
}
|
||||
}
|
||||
currentValues.checkForUnresolvedReferences();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given theme to set of all themes.
|
||||
* @param newTheme the theme to add
|
||||
*/
|
||||
public static void addTheme(GTheme newTheme) {
|
||||
loadThemes();
|
||||
allThemes.remove(newTheme);
|
||||
allThemes.add(newTheme);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the theme from the set of all themes. Also, if the theme has an associated
|
||||
* file, the file will be deleted.
|
||||
* @param theme the theme to delete
|
||||
*/
|
||||
public static void deleteTheme(GTheme theme) {
|
||||
File file = theme.getFile();
|
||||
if (file != null) {
|
||||
file.delete();
|
||||
}
|
||||
if (allThemes != null) {
|
||||
allThemes.remove(theme);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a set of all known themes.
|
||||
* @return a set of all known themes.
|
||||
*/
|
||||
public static Set<GTheme> getAllThemes() {
|
||||
loadThemes();
|
||||
return new HashSet<>(allThemes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a set of all known themes that are supported on the current platform.
|
||||
* @return a set of all known themes that are supported on the current platform.
|
||||
*/
|
||||
public static Set<GTheme> getSupportedThemes() {
|
||||
loadThemes();
|
||||
Set<GTheme> supported = new HashSet<>();
|
||||
for (GTheme theme : allThemes) {
|
||||
if (theme.hasSupportedLookAndFeel()) {
|
||||
supported.add(theme);
|
||||
}
|
||||
}
|
||||
return supported;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the active theme.
|
||||
* @return the active theme.
|
||||
*/
|
||||
public static GTheme getActiveTheme() {
|
||||
return activeTheme;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link LafType} for the currently active {@link LookAndFeel}
|
||||
* @return the {@link LafType} for the currently active {@link LookAndFeel}
|
||||
*/
|
||||
public static LafType getLookAndFeelType() {
|
||||
return activeTheme.getLookAndFeelType();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the known theme that has the given name.
|
||||
* @param themeName the name of the theme to retrieve
|
||||
* @return the known theme that has the given name
|
||||
*/
|
||||
public static GTheme getTheme(String themeName) {
|
||||
Optional<GTheme> first =
|
||||
getAllThemes().stream().filter(t -> t.getName().equals(themeName)).findFirst();
|
||||
return first.orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link GThemeValueMap} of all current theme values.
|
||||
* @return a {@link GThemeValueMap} of all current theme values.
|
||||
*/
|
||||
public static GThemeValueMap getAllValues() {
|
||||
return new GThemeValueMap(currentValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the theme values as defined by the current theme, ignoring any unsaved changes that
|
||||
* are currently applied to the application.
|
||||
* @return the theme values as defined by the current theme, ignoring any unsaved changes that
|
||||
* are currently applied to the application.
|
||||
*/
|
||||
public static GThemeValueMap getThemeValues() {
|
||||
GThemeValueMap map = new GThemeValueMap();
|
||||
map.load(javaDefaults);
|
||||
map.load(systemValues);
|
||||
map.load(applicationDefaults);
|
||||
if (activeTheme.useDarkDefaults()) {
|
||||
map.load(applicationDarkDefaults);
|
||||
}
|
||||
map.load(activeTheme);
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link GThemeValueMap} contains all values that differ from the default
|
||||
* values (values defined by the {@link LookAndFeel} or in the theme.properties files.
|
||||
* @return a {@link GThemeValueMap} contains all values that differ from the defaults.
|
||||
*/
|
||||
public static GThemeValueMap getNonDefaultValues() {
|
||||
return currentValues.getChangedValues(getDefaults());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current {@link Font} associated with the given id. A default font will be
|
||||
* returned if the font can't be resolved and an error message will be printed to the console.
|
||||
@ -321,32 +52,7 @@ public class Gui {
|
||||
* @return the current {@link Font} associated with the given id.
|
||||
*/
|
||||
public static Font getFont(String id) {
|
||||
Font font = getFont(id, true);
|
||||
if (font == FontValue.LAST_RESORT_DEFAULT) {
|
||||
return null;
|
||||
}
|
||||
return font;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current {@link Font} associated with the given id.
|
||||
* @param id the id for the desired font
|
||||
* @param validate if true, will print an error message to the console if the id can't be
|
||||
* resolved
|
||||
* @return the current {@link Font} associated with the given id.
|
||||
*/
|
||||
public static Font getFont(String id, boolean validate) {
|
||||
FontValue font = currentValues.getFont(id);
|
||||
|
||||
if (font == null) {
|
||||
if (validate && isInitialized) {
|
||||
Throwable t = getFilteredTrace();
|
||||
Msg.error(Gui.class,
|
||||
"No color value registered for: '" + id + "'", t);
|
||||
}
|
||||
return DEFAULT_FONT;
|
||||
}
|
||||
return font.get(currentValues);
|
||||
return themeManager.getFont(id);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -356,221 +62,7 @@ public class Gui {
|
||||
* @return the {@link Color} registered for the given id.
|
||||
*/
|
||||
public static Color getColor(String id) {
|
||||
return getColor(id, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the current font for the given id.
|
||||
* @param id the font id to update to the new color
|
||||
* @param font the new font for the id
|
||||
*/
|
||||
public static void setFont(String id, Font font) {
|
||||
setFont(new FontValue(id, font));
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the current value for the font id in the newValue
|
||||
* @param newValue the new {@link FontValue} to install in the current values.
|
||||
*/
|
||||
public static void setFont(FontValue newValue) {
|
||||
FontValue currentValue = currentValues.getFont(newValue.getId());
|
||||
if (newValue.equals(currentValue)) {
|
||||
return;
|
||||
}
|
||||
updateChangedValuesMap(currentValue, newValue);
|
||||
|
||||
currentValues.addFont(newValue);
|
||||
notifyThemeChanged(new FontChangedThemeEvent(currentValues, newValue));
|
||||
|
||||
// update all java LookAndFeel fonts affected by this changed
|
||||
String id = newValue.getId();
|
||||
Set<String> changedFontIds = findChangedJavaFontIds(id);
|
||||
lookAndFeelManager.fontsChanged(changedFontIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the current color for the given id.
|
||||
* @param id the color id to update to the new color
|
||||
* @param color the new color for the id
|
||||
*/
|
||||
public static void setColor(String id, Color color) {
|
||||
if (color == null) {
|
||||
throw new IllegalArgumentException("Can't set theme value to null!");
|
||||
}
|
||||
if (color instanceof GColor gColor) {
|
||||
if (id.equals(gColor.getId())) {
|
||||
Msg.warn(Gui.class, "Attempted to set a color to a reference to itself!");
|
||||
return; // this would create a circular reference to itself, don't do it
|
||||
}
|
||||
}
|
||||
setColor(new ColorValue(id, color));
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the current value for the color id in the newValue
|
||||
* @param newValue the new {@link ColorValue} to install in the current values.
|
||||
*/
|
||||
public static void setColor(ColorValue newValue) {
|
||||
ColorValue currentValue = currentValues.getColor(newValue.getId());
|
||||
if (newValue.equals(currentValue)) {
|
||||
return;
|
||||
}
|
||||
updateChangedValuesMap(currentValue, newValue);
|
||||
currentValues.addColor(newValue);
|
||||
notifyThemeChanged(new ColorChangedThemeEvent(currentValues, newValue));
|
||||
|
||||
// now update the ui
|
||||
if (lookAndFeelManager != null) {
|
||||
lookAndFeelManager.colorsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the current {@link Icon} for the given id.
|
||||
* @param id the icon id to update to the new icon
|
||||
* @param icon the new {@link Icon} for the id
|
||||
*/
|
||||
public static void setIcon(String id, Icon icon) {
|
||||
setIcon(new IconValue(id, icon));
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the current value for the {@link Icon} id in the newValue
|
||||
* @param newValue the new {@link IconValue} to install in the current values.
|
||||
*/
|
||||
public static void setIcon(IconValue newValue) {
|
||||
IconValue currentValue = currentValues.getIcon(newValue.getId());
|
||||
if (newValue.equals(currentValue)) {
|
||||
return;
|
||||
}
|
||||
updateChangedValuesMap(currentValue, newValue);
|
||||
|
||||
currentValues.addIcon(newValue);
|
||||
notifyThemeChanged(new IconChangedThemeEvent(currentValues, newValue));
|
||||
|
||||
// now update the ui
|
||||
// update all java LookAndFeel icons affected by this changed
|
||||
String id = newValue.getId();
|
||||
Set<String> changedIconIds = findChangedJavaIconIds(id);
|
||||
Icon newIcon = newValue.get(currentValues);
|
||||
lookAndFeelManager.iconsChanged(changedIconIds, newIcon);
|
||||
}
|
||||
|
||||
/**
|
||||
* gets a UIResource version of the GColor for the given id. Using this method ensures that
|
||||
* the same instance is used for a given id. This combats some poor code in some of the
|
||||
* {@link LookAndFeel}s where the use == in some places to test for equals.
|
||||
* @param id the id to get a GColorUIResource for
|
||||
* @return a GColorUIResource for the given id
|
||||
*/
|
||||
public static GColorUIResource getGColorUiResource(String id) {
|
||||
GColorUIResource gColor = gColorMap.get(id);
|
||||
if (gColor == null) {
|
||||
gColor = new GColorUIResource(id);
|
||||
gColorMap.put(id, gColor);
|
||||
}
|
||||
return gColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* gets a UIResource version of the GIcon for the given id. Using this method ensures that
|
||||
* the same instance is used for a given id. This combats some poor code in some of the
|
||||
* {@link LookAndFeel}s where the use == in some places to test for equals.
|
||||
* @param id the id to get a {@link GIconUIResource} for
|
||||
* @return a GIconUIResource for the given id
|
||||
*/
|
||||
public static GIconUIResource getGIconUiResource(String id) {
|
||||
|
||||
GIconUIResource gIcon = gIconMap.get(id);
|
||||
if (gIcon == null) {
|
||||
gIcon = new GIconUIResource(id);
|
||||
gIconMap.put(id, gIcon);
|
||||
}
|
||||
return gIcon;
|
||||
}
|
||||
|
||||
// used by
|
||||
public static void setSystemDefaults(GThemeValueMap map) {
|
||||
systemValues = map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the map of JavaDefaults defined by the current {@link LookAndFeel}.
|
||||
* @param map the default theme values defined by the {@link LookAndFeel}
|
||||
*/
|
||||
public static void setJavaDefaults(GThemeValueMap map) {
|
||||
javaDefaults = map;
|
||||
buildCurrentValues();
|
||||
GColor.refreshAll();
|
||||
GIcon.refreshAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link GThemeValueMap} containing all the default theme values defined by the
|
||||
* current {@link LookAndFeel}.
|
||||
* @return the {@link GThemeValueMap} containing all the default theme values defined by the
|
||||
* current {@link LookAndFeel}
|
||||
*/
|
||||
public static GThemeValueMap getJavaDefaults() {
|
||||
GThemeValueMap map = new GThemeValueMap();
|
||||
map.load(javaDefaults);
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link GThemeValueMap} containing all the dark default values defined
|
||||
* in theme.properties files. Note that dark defaults includes light defaults that haven't
|
||||
* been overridden by a dark default with the same id.
|
||||
* @return the {@link GThemeValueMap} containing all the dark values defined in
|
||||
* theme.properties files
|
||||
*/
|
||||
public static GThemeValueMap getApplicationDarkDefaults() {
|
||||
GThemeValueMap map = new GThemeValueMap(applicationDefaults);
|
||||
map.load(applicationDarkDefaults);
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link GThemeValueMap} containing all the standard default values defined
|
||||
* in theme.properties files.
|
||||
* @return the {@link GThemeValueMap} containing all the standard values defined in
|
||||
* theme.properties files
|
||||
*/
|
||||
public static GThemeValueMap getApplicationLightDefaults() {
|
||||
GThemeValueMap map = new GThemeValueMap(applicationDefaults);
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link GThemeValueMap} containing all default values for the current theme. It
|
||||
* is a combination of application defined defaults and java {@link LookAndFeel} defaults.
|
||||
* @return the current set of defaults.
|
||||
*/
|
||||
public static GThemeValueMap getDefaults() {
|
||||
GThemeValueMap currentDefaults = new GThemeValueMap(javaDefaults);
|
||||
currentDefaults.load(systemValues);
|
||||
currentDefaults.load(applicationDefaults);
|
||||
if (activeTheme.useDarkDefaults()) {
|
||||
currentDefaults.load(applicationDarkDefaults);
|
||||
}
|
||||
return currentDefaults;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given UI object is using the Aqua Look and Feel.
|
||||
* @param UI the UI to examine.
|
||||
* @return true if the UI is using Aqua
|
||||
*/
|
||||
public static boolean isUsingAquaUI(ComponentUI UI) {
|
||||
return activeTheme.getLookAndFeelType() == LafType.MAC;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if 'Nimbus' is the current Look and Feel
|
||||
* @return true if 'Nimbus' is the current Look and Feel
|
||||
*/
|
||||
public static boolean isUsingNimbusUI() {
|
||||
return activeTheme.getLookAndFeelType() == LafType.NIMBUS;
|
||||
return themeManager.getColor(id);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -578,7 +70,7 @@ public class Gui {
|
||||
* @param listener the listener to be notified
|
||||
*/
|
||||
public static void addThemeListener(ThemeListener listener) {
|
||||
themeListeners.add(listener);
|
||||
themeManager.addThemeListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -587,54 +79,7 @@ public class Gui {
|
||||
* @param listener the listener to be removed
|
||||
*/
|
||||
public static void removeThemeListener(ThemeListener listener) {
|
||||
themeListeners.add(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default theme for the current platform.
|
||||
* @return the default theme for the current platform.
|
||||
*/
|
||||
public static GTheme getDefaultTheme() {
|
||||
OperatingSystem OS = Platform.CURRENT_PLATFORM.getOperatingSystem();
|
||||
switch (OS) {
|
||||
case MAC_OS_X:
|
||||
return new MacTheme();
|
||||
case WINDOWS:
|
||||
return new WindowsTheme();
|
||||
case LINUX:
|
||||
case UNSUPPORTED:
|
||||
default:
|
||||
return new NimbusTheme();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if there are any unsaved changes to the current theme.
|
||||
* @return true if there are any unsaved changes to the current theme.
|
||||
*/
|
||||
public static boolean hasThemeChanges() {
|
||||
return !changedValuesMap.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the color for the id. If there is no color registered for this id, then Color.CYAN
|
||||
* is returned as the default color.
|
||||
* @param id the id to get the direct color for
|
||||
* @param validate if true, will output an error if the id can't be resolved at this time
|
||||
* @return the actual direct color for the id, not a GColor
|
||||
*/
|
||||
public static Color getColor(String id, boolean validate) {
|
||||
ColorValue color = currentValues.getColor(id);
|
||||
|
||||
if (color == null) {
|
||||
if (validate && isInitialized) {
|
||||
Throwable t = getFilteredTrace();
|
||||
Msg.error(Gui.class,
|
||||
"No color value registered for: '" + id + "'", t);
|
||||
}
|
||||
return Color.CYAN;
|
||||
}
|
||||
return color.get(currentValues);
|
||||
themeManager.removeThemeListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -644,7 +89,7 @@ public class Gui {
|
||||
* @return the actual icon registered for the given id
|
||||
*/
|
||||
public static Icon getIcon(String id) {
|
||||
return getIcon(id, true);
|
||||
return themeManager.getIcon(id);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -653,7 +98,7 @@ public class Gui {
|
||||
* @return true if an color for the given Id has been defined
|
||||
*/
|
||||
public static boolean hasColor(String id) {
|
||||
return currentValues.containsColor(id);
|
||||
return themeManager.hasColor(id);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -662,7 +107,7 @@ public class Gui {
|
||||
* @return true if an font for the given Id has been defined
|
||||
*/
|
||||
public static boolean hasFont(String id) {
|
||||
return currentValues.containsFont(id);
|
||||
return themeManager.hasFont(id);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -671,27 +116,7 @@ public class Gui {
|
||||
* @return true if an icon for the given Id has been defined
|
||||
*/
|
||||
public static boolean hasIcon(String id) {
|
||||
return currentValues.containsIcon(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link Icon} registered for the given id. If no icon is registered, returns
|
||||
* the default icon (bomb).
|
||||
* @param id the id to get the register icon for
|
||||
* @param validate if true, will output an error if the id can't be resolved at this time
|
||||
* @return the Icon registered for the given id
|
||||
*/
|
||||
public static Icon getIcon(String id, boolean validate) {
|
||||
IconValue icon = currentValues.getIcon(id);
|
||||
if (icon == null) {
|
||||
if (validate && isInitialized) {
|
||||
Throwable t = getFilteredTrace();
|
||||
Msg.error(Gui.class,
|
||||
"No icon value registered for: '" + id + "'", t);
|
||||
}
|
||||
return ResourceManager.getDefaultIcon();
|
||||
}
|
||||
return icon.get(currentValues);
|
||||
return themeManager.hasIcon(id);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -700,7 +125,7 @@ public class Gui {
|
||||
* @return a darker version of the given color or brighter if the current theme is dark
|
||||
*/
|
||||
public static Color darker(Color color) {
|
||||
if (activeTheme.useDarkDefaults()) {
|
||||
if (isDarkTheme()) {
|
||||
return color.brighter();
|
||||
}
|
||||
return color.darker();
|
||||
@ -712,7 +137,7 @@ public class Gui {
|
||||
* @return a brighter version of the given color or darker if the current theme is dark
|
||||
*/
|
||||
public static Color brighter(Color color) {
|
||||
if (activeTheme.useDarkDefaults()) {
|
||||
if (isDarkTheme()) {
|
||||
return color.darker();
|
||||
}
|
||||
return color.brighter();
|
||||
@ -725,137 +150,19 @@ public class Gui {
|
||||
* @param fontId the id of the font to register with the given component
|
||||
*/
|
||||
public static void registerFont(Component component, String fontId) {
|
||||
lookAndFeelManager.registerFont(component, fontId);
|
||||
themeManager.registerFont(component, fontId);
|
||||
}
|
||||
|
||||
private static void installFlatLookAndFeels() {
|
||||
UIManager.installLookAndFeel(LafType.FLAT_LIGHT.getName(), FlatLightLaf.class.getName());
|
||||
UIManager.installLookAndFeel(LafType.FLAT_DARK.getName(), FlatDarkLaf.class.getName());
|
||||
/**
|
||||
* Returns true if the active theme is using dark defaults
|
||||
* @return true if the active theme is using dark defaults
|
||||
*/
|
||||
public static boolean isDarkTheme() {
|
||||
return themeManager.isDarkTheme();
|
||||
}
|
||||
|
||||
private static void loadThemeDefaults() {
|
||||
themeFileLoader.loadThemeDefaultFiles();
|
||||
applicationDefaults = themeFileLoader.getDefaults();
|
||||
applicationDarkDefaults = themeFileLoader.getDarkDefaults();
|
||||
}
|
||||
|
||||
private static void notifyThemeChanged(ThemeEvent event) {
|
||||
for (ThemeListener listener : themeListeners) {
|
||||
listener.themeChanged(event);
|
||||
}
|
||||
}
|
||||
|
||||
private static Throwable getFilteredTrace() {
|
||||
Throwable t = ReflectionUtilities.createThrowableWithStackOlderThan();
|
||||
StackTraceElement[] trace = t.getStackTrace();
|
||||
StackTraceElement[] filtered =
|
||||
ReflectionUtilities.filterStackTrace(trace, "java.", "theme.Gui", "theme.GColor");
|
||||
t.setStackTrace(filtered);
|
||||
return t;
|
||||
}
|
||||
|
||||
private static void buildCurrentValues() {
|
||||
GThemeValueMap map = new GThemeValueMap();
|
||||
|
||||
map.load(javaDefaults);
|
||||
map.load(systemValues);
|
||||
map.load(applicationDefaults);
|
||||
if (activeTheme.useDarkDefaults()) {
|
||||
map.load(applicationDarkDefaults);
|
||||
}
|
||||
map.load(activeTheme);
|
||||
currentValues = map;
|
||||
changedValuesMap.clear();
|
||||
}
|
||||
|
||||
private static void loadThemes() {
|
||||
if (allThemes == null) {
|
||||
Set<GTheme> set = new HashSet<>();
|
||||
set.addAll(findDiscoverableThemes());
|
||||
set.addAll(themeFileLoader.loadThemeFiles());
|
||||
allThemes = set;
|
||||
}
|
||||
}
|
||||
|
||||
private static Collection<DiscoverableGTheme> findDiscoverableThemes() {
|
||||
return ClassSearcher.getInstances(DiscoverableGTheme.class);
|
||||
}
|
||||
|
||||
private static void updateChangedValuesMap(ColorValue currentValue, ColorValue newValue) {
|
||||
String id = newValue.getId();
|
||||
ColorValue originalValue = changedValuesMap.getColor(id);
|
||||
|
||||
// if new value is original value, it is no longer changed, remove it from changed map
|
||||
if (newValue.equals(originalValue)) {
|
||||
changedValuesMap.removeColor(id);
|
||||
}
|
||||
else if (originalValue == null) {
|
||||
// first time changed, so current value is original value
|
||||
changedValuesMap.addColor(currentValue);
|
||||
}
|
||||
}
|
||||
|
||||
private static void updateChangedValuesMap(FontValue currentValue, FontValue newValue) {
|
||||
String id = newValue.getId();
|
||||
FontValue originalValue = changedValuesMap.getFont(id);
|
||||
|
||||
// if new value is original value, it is no longer changed, remove it from changed map
|
||||
if (newValue.equals(originalValue)) {
|
||||
changedValuesMap.removeFont(id);
|
||||
}
|
||||
else if (originalValue == null) {
|
||||
// first time changed, so current value is original value
|
||||
changedValuesMap.addFont(currentValue);
|
||||
}
|
||||
}
|
||||
|
||||
private static void updateChangedValuesMap(IconValue currentValue, IconValue newValue) {
|
||||
String id = newValue.getId();
|
||||
IconValue originalValue = changedValuesMap.getIcon(id);
|
||||
|
||||
// if new value is original value, it is no longer changed, remove it from changed map
|
||||
if (newValue.equals(originalValue)) {
|
||||
changedValuesMap.removeIcon(id);
|
||||
}
|
||||
else if (originalValue == null) {
|
||||
// first time changed, so current value is original value
|
||||
changedValuesMap.addIcon(currentValue);
|
||||
}
|
||||
}
|
||||
|
||||
private static Set<String> findChangedJavaFontIds(String id) {
|
||||
Set<String> affectedIds = new HashSet<>();
|
||||
List<FontValue> fonts = javaDefaults.getFonts();
|
||||
for (FontValue fontValue : fonts) {
|
||||
String fontId = fontValue.getId();
|
||||
FontValue currentFontValue = currentValues.getFont(fontId);
|
||||
if (fontId.equals(id) || currentFontValue.inheritsFrom(id, currentValues)) {
|
||||
affectedIds.add(fontId);
|
||||
}
|
||||
}
|
||||
return affectedIds;
|
||||
}
|
||||
|
||||
private static Set<String> findChangedJavaIconIds(String id) {
|
||||
Set<String> affectedIds = new HashSet<>();
|
||||
List<IconValue> icons = javaDefaults.getIcons();
|
||||
for (IconValue iconValue : icons) {
|
||||
String iconId = iconValue.getId();
|
||||
if (iconId.equals(id) || iconValue.inheritsFrom(id, currentValues)) {
|
||||
affectedIds.add(iconId);
|
||||
}
|
||||
}
|
||||
return affectedIds;
|
||||
}
|
||||
|
||||
// for testing
|
||||
public static void setPropertiesLoader(ThemeFileLoader loader) {
|
||||
allThemes = null;
|
||||
themeFileLoader = loader;
|
||||
}
|
||||
|
||||
public static void setThemePreferenceManager(ThemePreferenceManager manager) {
|
||||
themePreferenceManager = manager;
|
||||
static void setThemeManager(ThemeManager manager) {
|
||||
themeManager = manager;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -247,8 +247,8 @@ public class IconValue extends ThemeValue<Icon> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void installValue() {
|
||||
Gui.setIcon(this);
|
||||
public void installValue(ThemeManager themeManager) {
|
||||
themeManager.setIcon(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -85,34 +85,36 @@ public enum LafType {
|
||||
/**
|
||||
* Returns a LookAndFeelManager that can install and update the {@link LookAndFeel} associated
|
||||
* with this LafType.
|
||||
* @param themeManager The application ThemeManager
|
||||
* @return a LookAndFeelManager that can install and update the {@link LookAndFeel} associated
|
||||
* with this LafType.
|
||||
*/
|
||||
public LookAndFeelManager getLookAndFeelManager() {
|
||||
return getManager(this);
|
||||
public LookAndFeelManager getLookAndFeelManager(ApplicationThemeManager themeManager) {
|
||||
return createManager(this, themeManager);
|
||||
}
|
||||
|
||||
private static LookAndFeelManager getManager(LafType lookAndFeel) {
|
||||
switch (lookAndFeel) {
|
||||
private static LookAndFeelManager createManager(LafType type,
|
||||
ApplicationThemeManager themeManager) {
|
||||
switch (type) {
|
||||
case MAC:
|
||||
return new MacLookAndFeelManager();
|
||||
return new MacLookAndFeelManager(themeManager);
|
||||
case METAL:
|
||||
return new MetalLookAndFeelManager();
|
||||
return new MetalLookAndFeelManager(themeManager);
|
||||
case WINDOWS:
|
||||
return new WindowsLookAndFeelManager();
|
||||
return new WindowsLookAndFeelManager(themeManager);
|
||||
case WINDOWS_CLASSIC:
|
||||
return new WindowsClassicLookAndFeelManager();
|
||||
return new WindowsClassicLookAndFeelManager(themeManager);
|
||||
case GTK:
|
||||
return new GtkLookAndFeelManager();
|
||||
return new GtkLookAndFeelManager(themeManager);
|
||||
case MOTIF:
|
||||
return new MotifLookAndFeelManager();
|
||||
return new MotifLookAndFeelManager(themeManager);
|
||||
case NIMBUS:
|
||||
return new NimbusLookAndFeelManager();
|
||||
return new NimbusLookAndFeelManager(themeManager);
|
||||
case FLAT_DARK:
|
||||
case FLAT_LIGHT:
|
||||
return new FlatLookAndFeelManager(lookAndFeel);
|
||||
return new FlatLookAndFeelManager(type, themeManager);
|
||||
default:
|
||||
throw new AssertException("No lookAndFeelManager defined for " + lookAndFeel);
|
||||
throw new AssertException("No lookAndFeelManager defined for " + type);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,227 @@
|
||||
/* ###
|
||||
* 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 generic.theme;
|
||||
|
||||
import static ghidra.util.WebColors.*;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.swing.plaf.ComponentUI;
|
||||
|
||||
/**
|
||||
* Version of ThemeManager that is used before an application or test installs a full
|
||||
* ApplicationThemeManager. Provides enough basic functionality used by the Gui class to
|
||||
* allow simple unit tests to run.
|
||||
*/
|
||||
public class StubThemeManager extends ThemeManager {
|
||||
|
||||
public StubThemeManager() {
|
||||
installPaletteColors();
|
||||
}
|
||||
|
||||
// palette colors are used statically throughout the application, so having them have values
|
||||
// in the stub will allow unit tests to run withouth initializing theming
|
||||
protected void installPaletteColors() {
|
||||
addPalette("nocolor", BLACK);
|
||||
addPalette("black", BLACK);
|
||||
addPalette("blue", BLUE);
|
||||
addPalette("cyan", CYAN);
|
||||
addPalette("darkgray", DARK_GRAY);
|
||||
addPalette("gold", GOLD);
|
||||
addPalette("gray", GRAY);
|
||||
addPalette("green", GREEN);
|
||||
addPalette("lavender", LAVENDER);
|
||||
addPalette("lightgray", LIGHT_GRAY);
|
||||
addPalette("lime", LIME);
|
||||
addPalette("magenta", MAGENTA);
|
||||
addPalette("maroon", MAROON);
|
||||
addPalette("orange", ORANGE);
|
||||
addPalette("pink", PINK);
|
||||
addPalette("purple", PURPLE);
|
||||
addPalette("red", RED);
|
||||
addPalette("silver", SILVER);
|
||||
addPalette("white", WHITE);
|
||||
addPalette("yellow", YELLOW);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reloadApplicationDefaults() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreThemeValues() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreColor(String id) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreFont(String id) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreIcon(String id) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChangedColor(String id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChangedFont(String id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChangedIcon(String id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTheme(GTheme theme) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTheme(GTheme newTheme) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteTheme(GTheme theme) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<GTheme> getAllThemes() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<GTheme> getSupportedThemes() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public GTheme getActiveTheme() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LafType getLookAndFeelType() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public GTheme getTheme(String themeName) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public GThemeValueMap getThemeValues() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFont(FontValue newValue) {
|
||||
currentValues.addFont(newValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setColor(ColorValue newValue) {
|
||||
currentValues.addColor(newValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIcon(IconValue newValue) {
|
||||
currentValues.addIcon(newValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GColorUIResource getGColorUiResource(String id) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public GIconUIResource getGIconUiResource(String id) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public GThemeValueMap getJavaDefaults() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public GThemeValueMap getApplicationDarkDefaults() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public GThemeValueMap getApplicationLightDefaults() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public GThemeValueMap getDefaults() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUsingAquaUI(ComponentUI UI) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUsingNimbusUI() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasThemeChanges() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerFont(Component component, String fontId) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDarkTheme() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void error(String message) {
|
||||
// don't report errors in stub for test purposes
|
||||
}
|
||||
|
||||
private void addPalette(String paletteId, Color color) {
|
||||
setColor(new ColorValue("color.palette." + paletteId, color));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,483 @@
|
||||
/* ###
|
||||
* 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 generic.theme;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.LookAndFeel;
|
||||
import javax.swing.plaf.ComponentUI;
|
||||
|
||||
import generic.theme.builtin.*;
|
||||
import ghidra.framework.OperatingSystem;
|
||||
import ghidra.framework.Platform;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.datastruct.WeakDataStructureFactory;
|
||||
import ghidra.util.datastruct.WeakSet;
|
||||
import resources.ResourceManager;
|
||||
import utilities.util.reflection.ReflectionUtilities;
|
||||
|
||||
/**
|
||||
* This class manages application themes and their values. The ThemeManager is an abstract
|
||||
* base class that has two concrete subclasses (and others for testing purposes) -
|
||||
* StubThemeManager and ApplicationThememManager. The StubThemeManager exists as a placeholder
|
||||
* until the ApplicationThemeManager is installed via {@link ApplicationThemeManager#initialize()}.
|
||||
* <P>
|
||||
* The basic idea is that all the colors, fonts, and icons used in an application should be
|
||||
* accessed indirectly via an "id" string. Then the actual color, font, or icon can be changed
|
||||
* without changing the source code. The default mapping of the id strings to a value is defined
|
||||
* in <name>.theme.properties files which are dynamically discovered by searching the module's
|
||||
* data directory. Also, these files can optionally define a dark default value for an id which
|
||||
* would replace the standard default value in the event that the current theme specifies that it
|
||||
* is a dark theme. Themes are used to specify the application's {@link LookAndFeel}, whether or
|
||||
* not it is dark, and any customized values for colors, fonts, or icons. There are several
|
||||
* "built-in" themes, one for each supported {@link LookAndFeel}, but additional themes can
|
||||
* be defined and stored in the users application home directory as a <name>.theme file.
|
||||
* <P>
|
||||
* Clients that just need to access the colors, fonts, and icons from the theme can use the
|
||||
* convenience methods in the {@link Gui} class. Clients that need to directly manipulate the
|
||||
* themes and values will need to directly use the ThemeManager which and be retrieved using the
|
||||
* static {@link #getInstance()} method.
|
||||
*/
|
||||
|
||||
public abstract class ThemeManager {
|
||||
static final Font DEFAULT_FONT = new Font("Dialog", Font.PLAIN, 12);
|
||||
static final Color DEFAULT_COLOR = Color.CYAN;
|
||||
|
||||
protected static ThemeManager INSTANCE;
|
||||
|
||||
protected GThemeValueMap currentValues = new GThemeValueMap();
|
||||
|
||||
// these notifications are only when the user is manipulating theme values, so rare and at
|
||||
// user speed, so using copy on read
|
||||
private WeakSet<ThemeListener> themeListeners =
|
||||
WeakDataStructureFactory.createCopyOnReadWeakSet();
|
||||
|
||||
public static ThemeManager getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
public ThemeManager() {
|
||||
if (INSTANCE == null) {
|
||||
// default behavior is only install to INSTANCE if first time
|
||||
INSTANCE = this;
|
||||
}
|
||||
}
|
||||
|
||||
protected void installInGui() {
|
||||
Gui.setThemeManager(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads the defaults from all the discoverable theme.property files.
|
||||
*/
|
||||
public abstract void reloadApplicationDefaults();
|
||||
|
||||
/**
|
||||
* Restores all the current application back to the values as specified by the active theme.
|
||||
* In other words, reverts any changes to the active theme that haven't been saved.
|
||||
*/
|
||||
public abstract void restoreThemeValues();
|
||||
|
||||
/**
|
||||
* Restores the current color value for the given color id to the value established by the
|
||||
* current theme.
|
||||
* @param id the color id to restore back to the original theme value
|
||||
*/
|
||||
public abstract void restoreColor(String id);
|
||||
|
||||
/**
|
||||
* Restores the current font value for the given font id to the value established by the
|
||||
* current theme.
|
||||
* @param id the font id to restore back to the original theme value
|
||||
*/
|
||||
public abstract void restoreFont(String id);
|
||||
|
||||
/**
|
||||
* Restores the current icon value for the given icon id to the value established by the
|
||||
* current theme.
|
||||
* @param id the icon id to restore back to the original theme value
|
||||
*/
|
||||
public abstract void restoreIcon(String id);
|
||||
|
||||
/**
|
||||
* Returns true if the color associated with the given id has been changed from the current
|
||||
* theme value for that id.
|
||||
* @param id the color id to check if it has been changed
|
||||
* @return true if the color associated with the given id has been changed from the current
|
||||
* theme value for that id.
|
||||
*/
|
||||
public abstract boolean isChangedColor(String id);
|
||||
|
||||
/**
|
||||
* Returns true if the font associated with the given id has been changed from the current
|
||||
* theme value for that id.
|
||||
* @param id the font id to check if it has been changed
|
||||
* @return true if the font associated with the given id has been changed from the current
|
||||
* theme value for that id.
|
||||
*/
|
||||
public abstract boolean isChangedFont(String id);
|
||||
|
||||
/**
|
||||
* Returns true if the Icon associated with the given id has been changed from the current
|
||||
* theme value for that id.
|
||||
* @param id the Icon id to check if it has been changed
|
||||
* @return true if the Icon associated with the given id has been changed from the current
|
||||
* theme value for that id.
|
||||
*/
|
||||
public abstract boolean isChangedIcon(String id);
|
||||
|
||||
/**
|
||||
* Sets the application's active theme to the given theme.
|
||||
* @param theme the theme to make active
|
||||
*/
|
||||
public abstract void setTheme(GTheme theme);
|
||||
|
||||
/**
|
||||
* Adds the given theme to set of all themes.
|
||||
* @param newTheme the theme to add
|
||||
*/
|
||||
public abstract void addTheme(GTheme newTheme);
|
||||
|
||||
/**
|
||||
* Removes the theme from the set of all themes. Also, if the theme has an associated
|
||||
* file, the file will be deleted.
|
||||
* @param theme the theme to delete
|
||||
*/
|
||||
public abstract void deleteTheme(GTheme theme);
|
||||
|
||||
/**
|
||||
* Returns a set of all known themes.
|
||||
* @return a set of all known themes.
|
||||
*/
|
||||
public abstract Set<GTheme> getAllThemes();
|
||||
|
||||
/**
|
||||
* Returns a set of all known themes that are supported on the current platform.
|
||||
* @return a set of all known themes that are supported on the current platform.
|
||||
*/
|
||||
public abstract Set<GTheme> getSupportedThemes();
|
||||
|
||||
/**
|
||||
* Returns the active theme.
|
||||
* @return the active theme.
|
||||
*/
|
||||
public abstract GTheme getActiveTheme();
|
||||
|
||||
/**
|
||||
* Returns the {@link LafType} for the currently active {@link LookAndFeel}
|
||||
* @return the {@link LafType} for the currently active {@link LookAndFeel}
|
||||
*/
|
||||
public abstract LafType getLookAndFeelType();
|
||||
|
||||
/**
|
||||
* Returns the known theme that has the given name.
|
||||
* @param themeName the name of the theme to retrieve
|
||||
* @return the known theme that has the given name
|
||||
*/
|
||||
public abstract GTheme getTheme(String themeName);
|
||||
|
||||
/**
|
||||
* Returns a {@link GThemeValueMap} of all current theme values including unsaved changes to the
|
||||
* theme.
|
||||
* @return a {@link GThemeValueMap} of all current theme values
|
||||
*/
|
||||
public GThemeValueMap getCurrentValues() {
|
||||
return new GThemeValueMap(currentValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the theme values as defined by the current theme, ignoring any unsaved changes that
|
||||
* are currently applied to the application.
|
||||
* @return the theme values as defined by the current theme, ignoring any unsaved changes that
|
||||
* are currently applied to the application
|
||||
*/
|
||||
public abstract GThemeValueMap getThemeValues();
|
||||
|
||||
/**
|
||||
* Returns a {@link GThemeValueMap} contains all values that differ from the default
|
||||
* values (values defined by the {@link LookAndFeel} or in the theme.properties files.
|
||||
* @return a {@link GThemeValueMap} contains all values that differ from the defaults.
|
||||
*/
|
||||
public GThemeValueMap getNonDefaultValues() {
|
||||
return currentValues.getChangedValues(getDefaults());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link Color} registered for the given id. Will output an error message if
|
||||
* the id can't be resolved.
|
||||
* @param id the id to get the direct color for
|
||||
* @return the {@link Color} registered for the given id.
|
||||
*/
|
||||
public Color getColor(String id) {
|
||||
ColorValue color = currentValues.getColor(id);
|
||||
|
||||
if (color == null) {
|
||||
error("No color value registered for: '" + id + "'");
|
||||
return DEFAULT_COLOR;
|
||||
}
|
||||
return color.get(currentValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current {@link Font} associated with the given id. A default font will be
|
||||
* returned if the font can't be resolved and an error message will be printed to the console.
|
||||
* @param id the id for the desired font
|
||||
* @return the current {@link Font} associated with the given id.
|
||||
*/
|
||||
public Font getFont(String id) {
|
||||
FontValue font = currentValues.getFont(id);
|
||||
|
||||
if (font == null) {
|
||||
error("No color value registered for: '" + id + "'");
|
||||
return DEFAULT_FONT;
|
||||
}
|
||||
return font.get(currentValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Icon registered for the given id. If no icon is registered for the id,
|
||||
* the default icon will be returned and an error message will be dumped to the console
|
||||
* @param id the id to get the registered icon for
|
||||
* @return the actual icon registered for the given id
|
||||
*/
|
||||
public Icon getIcon(String id) {
|
||||
IconValue icon = currentValues.getIcon(id);
|
||||
if (icon == null) {
|
||||
error("No icon value registered for: '" + id + "'");
|
||||
return ResourceManager.getDefaultIcon();
|
||||
}
|
||||
return icon.get(currentValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the current font for the given id.
|
||||
* @param id the font id to update to the new color
|
||||
* @param font the new font for the id
|
||||
*/
|
||||
public void setFont(String id, Font font) {
|
||||
setFont(new FontValue(id, font));
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the current value for the font id in the newValue
|
||||
* @param newValue the new {@link FontValue} to install in the current values.
|
||||
*/
|
||||
public abstract void setFont(FontValue newValue);
|
||||
|
||||
/**
|
||||
* Updates the current color for the given id.
|
||||
* @param id the color id to update to the new color
|
||||
* @param color the new color for the id
|
||||
*/
|
||||
public void setColor(String id, Color color) {
|
||||
if (color == null) {
|
||||
throw new IllegalArgumentException("Can't set theme value to null!");
|
||||
}
|
||||
if (color instanceof GColor gColor) {
|
||||
if (id.equals(gColor.getId())) {
|
||||
Throwable t = new Throwable();
|
||||
Msg.error(this, "Attempted to set a color for id \"" + id + "\" using a GColor" +
|
||||
" defined using that same id! This would create a self reference!", t);
|
||||
return; // this would create a circular reference to itself, don't do it
|
||||
}
|
||||
}
|
||||
setColor(new ColorValue(id, color));
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the current value for the color id in the newValue
|
||||
* @param newValue the new {@link ColorValue} to install in the current values.
|
||||
*/
|
||||
public abstract void setColor(ColorValue newValue);
|
||||
|
||||
/**
|
||||
* Updates the current {@link Icon} for the given id.
|
||||
* @param id the icon id to update to the new icon
|
||||
* @param icon the new {@link Icon} for the id
|
||||
*/
|
||||
public void setIcon(String id, Icon icon) {
|
||||
setIcon(new IconValue(id, icon));
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the current value for the {@link Icon} id in the newValue
|
||||
* @param newValue the new {@link IconValue} to install in the current values.
|
||||
*/
|
||||
public abstract void setIcon(IconValue newValue);
|
||||
|
||||
/**
|
||||
* gets a UIResource version of the GColor for the given id. Using this method ensures that
|
||||
* the same instance is used for a given id. This combats some poor code in some of the
|
||||
* {@link LookAndFeel}s where the use == in some places to test for equals.
|
||||
* @param id the id to get a GColorUIResource for
|
||||
* @return a GColorUIResource for the given id
|
||||
*/
|
||||
public abstract GColorUIResource getGColorUiResource(String id);
|
||||
|
||||
/**
|
||||
* gets a UIResource version of the GIcon for the given id. Using this method ensures that
|
||||
* the same instance is used for a given id. This combats some poor code in some of the
|
||||
* {@link LookAndFeel}s where the use == in some places to test for equals.
|
||||
* @param id the id to get a {@link GIconUIResource} for
|
||||
* @return a GIconUIResource for the given id
|
||||
*/
|
||||
public abstract GIconUIResource getGIconUiResource(String id);
|
||||
|
||||
/**
|
||||
* Returns the {@link GThemeValueMap} containing all the default theme values defined by the
|
||||
* current {@link LookAndFeel}.
|
||||
* @return the {@link GThemeValueMap} containing all the default theme values defined by the
|
||||
* current {@link LookAndFeel}
|
||||
*/
|
||||
public abstract GThemeValueMap getJavaDefaults();
|
||||
|
||||
/**
|
||||
* Returns the {@link GThemeValueMap} containing all the dark default values defined
|
||||
* in theme.properties files. Note that dark defaults includes light defaults that haven't
|
||||
* been overridden by a dark default with the same id.
|
||||
* @return the {@link GThemeValueMap} containing all the dark values defined in
|
||||
* theme.properties files
|
||||
*/
|
||||
public abstract GThemeValueMap getApplicationDarkDefaults();
|
||||
|
||||
/**
|
||||
* Returns the {@link GThemeValueMap} containing all the standard default values defined
|
||||
* in theme.properties files.
|
||||
* @return the {@link GThemeValueMap} containing all the standard values defined in
|
||||
* theme.properties files
|
||||
*/
|
||||
public abstract GThemeValueMap getApplicationLightDefaults();
|
||||
|
||||
/**
|
||||
* Returns a {@link GThemeValueMap} containing all default values for the current theme. It
|
||||
* is a combination of application defined defaults and java {@link LookAndFeel} defaults.
|
||||
* @return the current set of defaults.
|
||||
*/
|
||||
public abstract GThemeValueMap getDefaults();
|
||||
|
||||
/**
|
||||
* Returns true if the given UI object is using the Aqua Look and Feel.
|
||||
* @param UI the UI to examine.
|
||||
* @return true if the UI is using Aqua
|
||||
*/
|
||||
public abstract boolean isUsingAquaUI(ComponentUI UI);
|
||||
|
||||
/**
|
||||
* Returns true if 'Nimbus' is the current Look and Feel
|
||||
* @return true if 'Nimbus' is the current Look and Feel
|
||||
*/
|
||||
public abstract boolean isUsingNimbusUI();
|
||||
|
||||
/**
|
||||
* Adds a {@link ThemeListener} to be notified of theme changes.
|
||||
* @param listener the listener to be notified
|
||||
*/
|
||||
public void addThemeListener(ThemeListener listener) {
|
||||
themeListeners.add(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the given {@link ThemeListener} from the list of listeners to be notified of
|
||||
* theme changes.
|
||||
* @param listener the listener to be removed
|
||||
*/
|
||||
public void removeThemeListener(ThemeListener listener) {
|
||||
themeListeners.remove(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default theme for the current platform.
|
||||
* @return the default theme for the current platform.
|
||||
*/
|
||||
public static GTheme getDefaultTheme() {
|
||||
OperatingSystem OS = Platform.CURRENT_PLATFORM.getOperatingSystem();
|
||||
switch (OS) {
|
||||
case MAC_OS_X:
|
||||
return new MacTheme();
|
||||
case WINDOWS:
|
||||
return new WindowsTheme();
|
||||
case LINUX:
|
||||
case UNSUPPORTED:
|
||||
default:
|
||||
return new NimbusTheme();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if there are any unsaved changes to the current theme.
|
||||
* @return true if there are any unsaved changes to the current theme.
|
||||
*/
|
||||
public abstract boolean hasThemeChanges();
|
||||
|
||||
/**
|
||||
* Returns true if an color for the given Id has been defined
|
||||
* @param id the id to check for an existing color.
|
||||
* @return true if an color for the given Id has been defined
|
||||
*/
|
||||
public boolean hasColor(String id) {
|
||||
return currentValues.containsColor(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if an font for the given Id has been defined
|
||||
* @param id the id to check for an existing font.
|
||||
* @return true if an font for the given Id has been defined
|
||||
*/
|
||||
public boolean hasFont(String id) {
|
||||
return currentValues.containsFont(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if an icon for the given Id has been defined
|
||||
* @param id the id to check for an existing icon.
|
||||
* @return true if an icon for the given Id has been defined
|
||||
*/
|
||||
public boolean hasIcon(String id) {
|
||||
return currentValues.containsIcon(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds the component to the font identified by the given font id. Whenever the font for
|
||||
* the font id changes, the component will updated with the new font.
|
||||
* @param component the component to set/update the font
|
||||
* @param fontId the id of the font to register with the given component
|
||||
*/
|
||||
public abstract void registerFont(Component component, String fontId);
|
||||
|
||||
/**
|
||||
* Returns true if the current theme use dark default values.
|
||||
* @return true if the current theme use dark default values.
|
||||
*/
|
||||
public abstract boolean isDarkTheme();
|
||||
|
||||
protected void notifyThemeChanged(ThemeEvent event) {
|
||||
for (ThemeListener listener : themeListeners) {
|
||||
listener.themeChanged(event);
|
||||
}
|
||||
}
|
||||
|
||||
protected void error(String message) {
|
||||
Throwable t = ReflectionUtilities.createThrowableWithStackOlderThan();
|
||||
StackTraceElement[] trace = t.getStackTrace();
|
||||
StackTraceElement[] filtered =
|
||||
ReflectionUtilities.filterStackTrace(trace, "java.", "theme.Gui", "theme.ThemeManager",
|
||||
"theme.GColor");
|
||||
t.setStackTrace(filtered);
|
||||
Msg.error(this, message, t);
|
||||
}
|
||||
}
|
@ -24,7 +24,7 @@ import ghidra.util.Msg;
|
||||
/**
|
||||
* Reads and writes current theme info to preferences
|
||||
*/
|
||||
public class ThemePreferenceManager {
|
||||
public class ThemePreferences {
|
||||
private static final String THEME_PREFFERENCE_KEY = "Theme";
|
||||
|
||||
/**
|
||||
@ -32,7 +32,7 @@ public class ThemePreferenceManager {
|
||||
* @return the last theme used (stored in preferences) or the default theme if not stored
|
||||
* in preferences
|
||||
*/
|
||||
public GTheme getTheme() {
|
||||
public GTheme load() {
|
||||
String themeId = Preferences.getProperty(THEME_PREFFERENCE_KEY, "Default", true);
|
||||
if (themeId.startsWith(GTheme.FILE_PREFIX)) {
|
||||
String filename = themeId.substring(GTheme.FILE_PREFIX.length());
|
||||
@ -55,14 +55,14 @@ public class ThemePreferenceManager {
|
||||
"Can't find or instantiate class: " + className, e);
|
||||
}
|
||||
}
|
||||
return Gui.getDefaultTheme();
|
||||
return ThemeManager.getDefaultTheme();
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the current theme choice to {@link Preferences}.
|
||||
* @param theme the theme to remember in {@link Preferences}
|
||||
*/
|
||||
public void saveThemeToPreferences(GTheme theme) {
|
||||
public void save(GTheme theme) {
|
||||
Preferences.setProperty(THEME_PREFFERENCE_KEY, theme.getThemeLocater());
|
||||
Preferences.store();
|
||||
}
|
@ -73,7 +73,10 @@ public abstract class ThemeValue<T> implements Comparable<ThemeValue<T>> {
|
||||
|
||||
/**
|
||||
* Returns the T value for this instance, following references as needed. Uses the given
|
||||
* preferredValues map to resolve references.
|
||||
* preferredValues map to resolve references. If the value can't be resolved by following
|
||||
* reference chains, an error stack trace will be generated and the default T value will
|
||||
* be returned. In rare situations where it is acceptable for the value to not be resolvable,
|
||||
* use the {@link #hasResolvableValue(GThemeValueMap)} method first.
|
||||
* @param values the {@link GThemeValueMap} used to resolve references if this
|
||||
* instance doesn't have an actual value.
|
||||
* @return the T value for this instance, following references as needed.
|
||||
@ -85,23 +88,55 @@ public abstract class ThemeValue<T> implements Comparable<ThemeValue<T>> {
|
||||
|
||||
Set<String> visitedKeys = new HashSet<>();
|
||||
visitedKeys.add(id);
|
||||
ThemeValue<T> parent = getReferredValue(values, referenceId);
|
||||
ThemeValue<T> referred = getReferredValue(values, referenceId);
|
||||
|
||||
// loop resolving indirect references
|
||||
while (parent != null) {
|
||||
if (parent.value != null) {
|
||||
return parent.value;
|
||||
while (referred != null) {
|
||||
if (referred.value != null) {
|
||||
return referred.value;
|
||||
}
|
||||
visitedKeys.add(parent.id);
|
||||
if (visitedKeys.contains(parent.referenceId)) {
|
||||
visitedKeys.add(referred.id);
|
||||
if (visitedKeys.contains(referred.referenceId)) {
|
||||
Msg.warn(this, "Theme value reference loop detected for key: " + id);
|
||||
return getUnresolvedReferenceValue(id, parent.referenceId);
|
||||
return getUnresolvedReferenceValue(id, referred.referenceId);
|
||||
}
|
||||
parent = getReferredValue(values, parent.referenceId);
|
||||
referred = getReferredValue(values, referred.referenceId);
|
||||
}
|
||||
return getUnresolvedReferenceValue(id, referenceId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the ThemeValue can resolve to the concrete T value (color, font, or icon)
|
||||
* from the given set of theme values.
|
||||
* @param values the set of values to use to try and follow reference chains to ultimately
|
||||
* resolve the ThemeValue to a an actual T value
|
||||
* @return true if the ThemeValue can resolve to the concrete T value (color, font, or icon)
|
||||
* from the given set of theme values.
|
||||
*/
|
||||
public boolean hasResolvableValue(GThemeValueMap values) {
|
||||
if (value != null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Set<String> visitedKeys = new HashSet<>();
|
||||
visitedKeys.add(id);
|
||||
ThemeValue<T> referred = getReferredValue(values, referenceId);
|
||||
|
||||
// loop resolving indirect references
|
||||
while (referred != null) {
|
||||
if (referred.value != null) {
|
||||
return true;
|
||||
}
|
||||
visitedKeys.add(referred.id);
|
||||
if (visitedKeys.contains(referred.referenceId)) {
|
||||
Msg.warn(this, "Theme value reference loop detected for key: " + id);
|
||||
return false;
|
||||
}
|
||||
referred = getReferredValue(values, referred.referenceId);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this ThemeValue derives its value from the given ancestorId.
|
||||
* @param ancestorId the id to test if this Theme value inherits from
|
||||
@ -208,7 +243,8 @@ public abstract class ThemeValue<T> implements Comparable<ThemeValue<T>> {
|
||||
|
||||
/**
|
||||
* Install this value as the current value for the application
|
||||
* @param themeManager the application ThemeManager
|
||||
*/
|
||||
public abstract void installValue();
|
||||
public abstract void installValue(ThemeManager themeManager);
|
||||
|
||||
}
|
||||
|
@ -17,13 +17,12 @@ package generic.theme.laf;
|
||||
|
||||
import javax.swing.UIManager;
|
||||
|
||||
import generic.theme.ColorValue;
|
||||
import generic.theme.LafType;
|
||||
import generic.theme.*;
|
||||
|
||||
public class FlatLookAndFeelManager extends LookAndFeelManager {
|
||||
|
||||
public FlatLookAndFeelManager(LafType laf) {
|
||||
super(laf);
|
||||
public FlatLookAndFeelManager(LafType laf, ApplicationThemeManager themeManager) {
|
||||
super(laf, themeManager);
|
||||
|
||||
// establish system color to LookAndFeel colors
|
||||
systemToLafMap.addColor(new ColorValue(SYSTEM_WIDGET_BACKGROUND_COLOR_ID, "text"));
|
||||
|
@ -19,18 +19,32 @@ import java.awt.Color;
|
||||
import java.awt.Font;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.UIDefaults;
|
||||
import javax.swing.*;
|
||||
import javax.swing.plaf.FontUIResource;
|
||||
import javax.swing.plaf.nimbus.NimbusLookAndFeel;
|
||||
|
||||
import generic.theme.*;
|
||||
|
||||
/**
|
||||
* Extends the NimbusLookAndFeel to intercept the {@link #getDefaults()}. To get Nimbus
|
||||
* to use our indirect values, we have to get in early.
|
||||
* Extends the {@link NimbusLookAndFeel} to intercept the {@link #getDefaults()}. Nimbus does
|
||||
* not honor changes to the UIDefaults after it is installed as the active
|
||||
* {@link LookAndFeel}, so we have to make the changes at the time the UIDefaults are installed.
|
||||
*
|
||||
* To get around this issue, we extend the NimbusLookAndFeel
|
||||
* so that we can install our GColors and overridden properties as Nimbus is being installed,
|
||||
* specifically during the call to the getDefaults() method. For all other Look And Feels, the
|
||||
* GColors and overridden properties are changed in the UIDefaults after the Look And Feel is
|
||||
* installed, so they don't need to extends the Look and Feel class.
|
||||
*
|
||||
* Also, note that Nimbus needs to be reinstalled every time we need to make a change to any of the
|
||||
* UIDefaults values, since it does not respond to changes other than when first installed.
|
||||
*/
|
||||
public class GNimbusLookAndFeel extends NimbusLookAndFeel {
|
||||
private ApplicationThemeManager themeManager;
|
||||
|
||||
GNimbusLookAndFeel(ApplicationThemeManager themeManager) {
|
||||
this.themeManager = themeManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UIDefaults getDefaults() {
|
||||
@ -40,13 +54,13 @@ public class GNimbusLookAndFeel extends NimbusLookAndFeel {
|
||||
// replace all colors with GColors
|
||||
for (ColorValue colorValue : javaDefaults.getColors()) {
|
||||
String id = colorValue.getId();
|
||||
defaults.put(id, Gui.getGColorUiResource(id));
|
||||
defaults.put(id, themeManager.getGColorUiResource(id));
|
||||
}
|
||||
|
||||
// put fonts back into defaults in case they have been changed by the current theme
|
||||
for (FontValue fontValue : javaDefaults.getFonts()) {
|
||||
String id = fontValue.getId();
|
||||
Font font = Gui.getFont(id);
|
||||
Font font = themeManager.getFont(id);
|
||||
defaults.put(id, new FontUIResource(font));
|
||||
}
|
||||
|
||||
@ -55,13 +69,12 @@ public class GNimbusLookAndFeel extends NimbusLookAndFeel {
|
||||
String id = iconValue.getId();
|
||||
// because some icons are weird, put raw icons into defaults, only use GIcons for
|
||||
// setting Icons explicitly on components
|
||||
Icon icon = Gui.getIcon(id, true);
|
||||
Icon icon = themeManager.getIcon(id);
|
||||
defaults.put(id, icon);
|
||||
}
|
||||
|
||||
defaults.put("Label.textForeground", Gui.getGColorUiResource("Label.foreground"));
|
||||
GColor.refreshAll();
|
||||
GIcon.refreshAll();
|
||||
defaults.put("Label.textForeground", themeManager.getGColorUiResource("Label.foreground"));
|
||||
themeManager.refreshGThemeValues();
|
||||
return defaults;
|
||||
}
|
||||
|
||||
@ -90,7 +103,7 @@ public class GNimbusLookAndFeel extends NimbusLookAndFeel {
|
||||
}
|
||||
// need to set javaDefalts now to trigger building currentValues so the when
|
||||
// we create GColors below, they can be resolved.
|
||||
Gui.setJavaDefaults(javaDefaults);
|
||||
themeManager.setJavaDefaults(javaDefaults);
|
||||
return javaDefaults;
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,7 @@
|
||||
*/
|
||||
package generic.theme.laf;
|
||||
|
||||
import generic.theme.ApplicationThemeManager;
|
||||
import generic.theme.LafType;
|
||||
|
||||
/**
|
||||
@ -22,7 +23,7 @@ import generic.theme.LafType;
|
||||
*/
|
||||
public class GtkLookAndFeelManager extends LookAndFeelManager {
|
||||
|
||||
public GtkLookAndFeelManager() {
|
||||
super(LafType.GTK);
|
||||
public GtkLookAndFeelManager(ApplicationThemeManager themeManager) {
|
||||
super(LafType.GTK, themeManager);
|
||||
}
|
||||
}
|
||||
|
@ -51,9 +51,11 @@ public abstract class LookAndFeelManager {
|
||||
private LafType laf;
|
||||
private Map<String, ComponentFontRegistry> fontRegistryMap = new HashMap<>();
|
||||
protected GThemeValueMap systemToLafMap = new GThemeValueMap();
|
||||
protected ApplicationThemeManager themeManager;
|
||||
|
||||
protected LookAndFeelManager(LafType laf) {
|
||||
protected LookAndFeelManager(LafType laf, ApplicationThemeManager themeManager) {
|
||||
this.laf = laf;
|
||||
this.themeManager = themeManager;
|
||||
|
||||
// establish system color to LookAndFeel colors
|
||||
systemToLafMap.addColor(new ColorValue(SYSTEM_APP_BACKGROUND_COLOR_ID, "control"));
|
||||
@ -84,7 +86,7 @@ public abstract class LookAndFeelManager {
|
||||
IllegalAccessException, UnsupportedLookAndFeelException {
|
||||
|
||||
cleanUiDefaults();
|
||||
Gui.setSystemDefaults(systemToLafMap);
|
||||
themeManager.setSystemDefaults(systemToLafMap);
|
||||
doInstallLookAndFeel();
|
||||
installJavaDefaults();
|
||||
fixupLookAndFeelIssues();
|
||||
@ -98,8 +100,7 @@ public abstract class LookAndFeelManager {
|
||||
* special as needed by the current {@link LookAndFeel}
|
||||
*/
|
||||
public void resetAll(GThemeValueMap javaDefaults) {
|
||||
GColor.refreshAll();
|
||||
GIcon.refreshAll();
|
||||
themeManager.refreshGThemeValues();
|
||||
resetIcons(javaDefaults);
|
||||
resetFonts(javaDefaults);
|
||||
updateAllRegisteredComponentFonts();
|
||||
@ -130,7 +131,7 @@ public abstract class LookAndFeelManager {
|
||||
UIDefaults defaults = UIManager.getDefaults();
|
||||
for (IconValue iconValue : icons) {
|
||||
String id = iconValue.getId();
|
||||
Icon correctIcon = Gui.getIcon(id, false);
|
||||
Icon correctIcon = Gui.getIcon(id);
|
||||
Icon storedIcon = defaults.getIcon(id);
|
||||
if (correctIcon != null && !correctIcon.equals(storedIcon)) {
|
||||
defaults.put(id, correctIcon);
|
||||
@ -142,7 +143,7 @@ public abstract class LookAndFeelManager {
|
||||
* Called when one or more colors have changed.
|
||||
*/
|
||||
public void colorsChanged() {
|
||||
GColor.refreshAll();
|
||||
themeManager.refreshGThemeValues();
|
||||
repaintAll();
|
||||
}
|
||||
|
||||
@ -162,7 +163,7 @@ public abstract class LookAndFeelManager {
|
||||
}
|
||||
updateComponentUis();
|
||||
}
|
||||
GIcon.refreshAll();
|
||||
themeManager.refreshGThemeValues();
|
||||
repaintAll();
|
||||
}
|
||||
|
||||
@ -265,7 +266,7 @@ public abstract class LookAndFeelManager {
|
||||
GThemeValueMap javaDefaults = extractJavaDefaults();
|
||||
ThemeGrouper grouper = getThemeGrouper();
|
||||
grouper.group(javaDefaults);
|
||||
Gui.setJavaDefaults(javaDefaults);
|
||||
themeManager.setJavaDefaults(javaDefaults);
|
||||
installPropertiesBackIntoUiDefaults(javaDefaults);
|
||||
}
|
||||
|
||||
@ -276,7 +277,7 @@ public abstract class LookAndFeelManager {
|
||||
private void installPropertiesBackIntoUiDefaults(GThemeValueMap javaDefaults) {
|
||||
UIDefaults defaults = UIManager.getDefaults();
|
||||
|
||||
GTheme theme = Gui.getActiveTheme();
|
||||
GTheme theme = themeManager.getActiveTheme();
|
||||
|
||||
// we replace java default colors with GColor equivalents so that we
|
||||
// can change colors without having to reinstall ui on each component
|
||||
@ -284,7 +285,7 @@ public abstract class LookAndFeelManager {
|
||||
// allow being wrapped like colors do.
|
||||
for (ColorValue colorValue : javaDefaults.getColors()) {
|
||||
String id = colorValue.getId();
|
||||
defaults.put(id, Gui.getGColorUiResource(id));
|
||||
defaults.put(id, themeManager.getGColorUiResource(id));
|
||||
}
|
||||
|
||||
// put fonts back into defaults in case they have been changed by the current theme
|
||||
@ -304,7 +305,7 @@ public abstract class LookAndFeelManager {
|
||||
if (themeValue != null) {
|
||||
// because some icons are weird, put raw icons into defaults, only use GIcons for
|
||||
// setting Icons explicitly on components
|
||||
Icon icon = Gui.getIcon(id, true);
|
||||
Icon icon = Gui.getIcon(id);
|
||||
defaults.put(id, icon);
|
||||
}
|
||||
}
|
||||
@ -463,7 +464,7 @@ public abstract class LookAndFeelManager {
|
||||
}
|
||||
|
||||
private void cleanUiDefaults() {
|
||||
GThemeValueMap javaDefaults = Gui.getJavaDefaults();
|
||||
GThemeValueMap javaDefaults = themeManager.getJavaDefaults();
|
||||
if (javaDefaults == null) {
|
||||
return;
|
||||
}
|
||||
|
@ -15,12 +15,13 @@
|
||||
*/
|
||||
package generic.theme.laf;
|
||||
|
||||
import generic.theme.ApplicationThemeManager;
|
||||
import generic.theme.LafType;
|
||||
|
||||
public class MacLookAndFeelManager extends LookAndFeelManager {
|
||||
|
||||
public MacLookAndFeelManager() {
|
||||
super(LafType.MAC);
|
||||
public MacLookAndFeelManager(ApplicationThemeManager themeManager) {
|
||||
super(LafType.MAC, themeManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -15,11 +15,12 @@
|
||||
*/
|
||||
package generic.theme.laf;
|
||||
|
||||
import generic.theme.ApplicationThemeManager;
|
||||
import generic.theme.LafType;
|
||||
|
||||
public class MetalLookAndFeelManager extends LookAndFeelManager {
|
||||
|
||||
public MetalLookAndFeelManager() {
|
||||
super(LafType.METAL);
|
||||
public MetalLookAndFeelManager(ApplicationThemeManager themeManager) {
|
||||
super(LafType.METAL, themeManager);
|
||||
}
|
||||
}
|
||||
|
@ -15,16 +15,15 @@
|
||||
*/
|
||||
package generic.theme.laf;
|
||||
|
||||
import generic.theme.ColorValue;
|
||||
import generic.theme.LafType;
|
||||
import generic.theme.*;
|
||||
|
||||
/**
|
||||
* Motif {@link LookAndFeelManager}. Specialized so that it can return the Motif installer
|
||||
*/
|
||||
public class MotifLookAndFeelManager extends LookAndFeelManager {
|
||||
|
||||
public MotifLookAndFeelManager() {
|
||||
super(LafType.MOTIF);
|
||||
public MotifLookAndFeelManager(ApplicationThemeManager themeManager) {
|
||||
super(LafType.MOTIF, themeManager);
|
||||
// establish system color to LookAndFeel colors
|
||||
systemToLafMap.addColor(new ColorValue(SYSTEM_APP_BACKGROUND_COLOR_ID, "control"));
|
||||
systemToLafMap.addColor(new ColorValue(SYSTEM_WIDGET_BACKGROUND_COLOR_ID, "window"));
|
||||
|
@ -30,8 +30,8 @@ import ghidra.util.exception.AssertException;
|
||||
*/
|
||||
public class NimbusLookAndFeelManager extends LookAndFeelManager {
|
||||
|
||||
public NimbusLookAndFeelManager() {
|
||||
super(LafType.NIMBUS);
|
||||
public NimbusLookAndFeelManager(ApplicationThemeManager themeManager) {
|
||||
super(LafType.NIMBUS, themeManager);
|
||||
|
||||
// establish system color specific to Nimbus
|
||||
systemToLafMap.addColor(new ColorValue(SYSTEM_BORDER_COLOR_ID, "nimbusBorder"));
|
||||
@ -39,8 +39,7 @@ public class NimbusLookAndFeelManager extends LookAndFeelManager {
|
||||
|
||||
@Override
|
||||
public void resetAll(GThemeValueMap javaDefaults) {
|
||||
GColor.refreshAll();
|
||||
GIcon.refreshAll();
|
||||
themeManager.refreshGThemeValues();
|
||||
reinstallNimubus();
|
||||
}
|
||||
|
||||
@ -58,16 +57,16 @@ public class NimbusLookAndFeelManager extends LookAndFeelManager {
|
||||
if (!affectedJavaIds.isEmpty()) {
|
||||
reinstallNimubus();
|
||||
}
|
||||
GIcon.refreshAll();
|
||||
themeManager.refreshGThemeValues();
|
||||
repaintAll();
|
||||
}
|
||||
|
||||
private void reinstallNimubus() {
|
||||
try {
|
||||
UIManager.setLookAndFeel(new GNimbusLookAndFeel() {
|
||||
UIManager.setLookAndFeel(new GNimbusLookAndFeel(themeManager) {
|
||||
@Override
|
||||
protected GThemeValueMap extractJavaDefaults(UIDefaults defaults) {
|
||||
return Gui.getJavaDefaults();
|
||||
return themeManager.getJavaDefaults();
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -79,13 +78,13 @@ public class NimbusLookAndFeelManager extends LookAndFeelManager {
|
||||
|
||||
@Override
|
||||
protected void doInstallLookAndFeel() throws UnsupportedLookAndFeelException {
|
||||
UIManager.setLookAndFeel(new GNimbusLookAndFeel());
|
||||
UIManager.setLookAndFeel(new GNimbusLookAndFeel(themeManager));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GThemeValueMap extractJavaDefaults() {
|
||||
// The GNimbusLookAndFeel already extracted the java defaults and installed them in the Gui
|
||||
return Gui.getJavaDefaults();
|
||||
return themeManager.getJavaDefaults();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -15,11 +15,12 @@
|
||||
*/
|
||||
package generic.theme.laf;
|
||||
|
||||
import generic.theme.ApplicationThemeManager;
|
||||
import generic.theme.LafType;
|
||||
|
||||
public class WindowsClassicLookAndFeelManager extends LookAndFeelManager {
|
||||
|
||||
public WindowsClassicLookAndFeelManager() {
|
||||
super(LafType.WINDOWS_CLASSIC);
|
||||
public WindowsClassicLookAndFeelManager(ApplicationThemeManager themeManager) {
|
||||
super(LafType.WINDOWS_CLASSIC, themeManager);
|
||||
}
|
||||
}
|
||||
|
@ -15,12 +15,13 @@
|
||||
*/
|
||||
package generic.theme.laf;
|
||||
|
||||
import generic.theme.ApplicationThemeManager;
|
||||
import generic.theme.LafType;
|
||||
|
||||
public class WindowsLookAndFeelManager extends LookAndFeelManager {
|
||||
|
||||
public WindowsLookAndFeelManager() {
|
||||
super(LafType.WINDOWS);
|
||||
public WindowsLookAndFeelManager(ApplicationThemeManager themeManager) {
|
||||
super(LafType.WINDOWS, themeManager);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -108,7 +108,14 @@ public interface Options {
|
||||
/**
|
||||
* Registers an option with a description, help location, and a default value without specifying
|
||||
* the option type. This form requires that the default value not be null so that the option
|
||||
* type can be inferred from the default value.
|
||||
* type can be inferred from the default value.
|
||||
* <P>
|
||||
* Note, this method should not be used for
|
||||
* colors and font as doing so will result in those colors and fonts becoming disconnected
|
||||
* to the current theme. Instead use
|
||||
*
|
||||
* {@link #registerThemeColorBinding(String, String, HelpLocation, String)} or
|
||||
* {@link #registerThemeFontBinding(String, String, HelpLocation, String)}.
|
||||
* @param optionName the name of the option being registered.
|
||||
* @param defaultValue the defaultValue for the option. The default value must not be
|
||||
* null so that the OptionType can be determined. If the default value should be null, use
|
||||
@ -123,6 +130,13 @@ public interface Options {
|
||||
/**
|
||||
* Registers an option with a description, help location, and a optional default value. With an optional
|
||||
* default value, an OptionType must be passed as it is otherwise derived from the default value.
|
||||
* <P>
|
||||
* Note, this method should not be used for
|
||||
* colors and font as doing so will result in those colors and fonts becoming disconnected
|
||||
* to the current theme. Instead use
|
||||
* {@link #registerThemeColorBinding(String, String, HelpLocation, String)} or
|
||||
* {@link #registerThemeFontBinding(String, String, HelpLocation, String)}.
|
||||
*
|
||||
* @param optionName the name of the option being registered.
|
||||
* @param type the OptionType for this options.
|
||||
* @param defaultValue the defaultValue for the option. In this version of the method, the default
|
||||
@ -136,6 +150,13 @@ public interface Options {
|
||||
/**
|
||||
* Registers an option with a description, help location, and a optional default value. With an optional
|
||||
* default value, an OptionType must be passed as it is otherwise derived from the default value.
|
||||
* <P>
|
||||
* Note, this method should not be used for
|
||||
* colors and font as doing so will result in those colors and fonts becoming disconnected
|
||||
* to the current theme. Instead use
|
||||
* {@link #registerThemeColorBinding(String, String, HelpLocation, String)} or
|
||||
* {@link #registerThemeFontBinding(String, String, HelpLocation, String)}.
|
||||
*
|
||||
* @param optionName the name of the option being registered.
|
||||
* @param type the OptionType for this options.
|
||||
* @param defaultValue the defaultValue for the option. In this version of the method, the default
|
||||
|
@ -17,8 +17,7 @@ package ghidra.framework.options;
|
||||
|
||||
import java.awt.Color;
|
||||
|
||||
import generic.theme.GColor;
|
||||
import generic.theme.Gui;
|
||||
import generic.theme.*;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
@ -55,17 +54,17 @@ public class ThemeColorOption extends Option {
|
||||
|
||||
@Override
|
||||
public void doSetCurrentValue(Object value) {
|
||||
Gui.setColor(colorId, (Color) value);
|
||||
ThemeManager.getInstance().setColor(colorId, (Color) value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDefault() {
|
||||
return !Gui.isChangedColor(colorId);
|
||||
return !ThemeManager.getInstance().isChangedColor(colorId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreDefault() {
|
||||
Gui.restoreColor(colorId);
|
||||
ThemeManager.getInstance().restoreColor(colorId);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ package ghidra.framework.options;
|
||||
import java.awt.Font;
|
||||
|
||||
import generic.theme.Gui;
|
||||
import generic.theme.ThemeManager;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
@ -54,17 +55,17 @@ public class ThemeFontOption extends Option {
|
||||
|
||||
@Override
|
||||
public void doSetCurrentValue(Object value) {
|
||||
Gui.setFont(fontId, (Font) value);
|
||||
ThemeManager.getInstance().setFont(fontId, (Font) value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDefault() {
|
||||
return !Gui.isChangedFont(fontId);
|
||||
return !ThemeManager.getInstance().isChangedFont(fontId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreDefault() {
|
||||
Gui.restoreFont(fontId);
|
||||
ThemeManager.getInstance().restoreFont(fontId);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ import java.util.Objects;
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.ImageIcon;
|
||||
|
||||
import generic.theme.GIcon;
|
||||
import generic.util.image.ImageUtils;
|
||||
import resources.ResourceManager;
|
||||
|
||||
@ -32,6 +33,7 @@ import resources.ResourceManager;
|
||||
public class DerivedImageIcon extends LazyImageIcon {
|
||||
private Icon sourceIcon;
|
||||
private Image sourceImage;
|
||||
private Icon cachedDelegate;
|
||||
|
||||
/**
|
||||
* Constructor for deriving from an icon
|
||||
@ -56,6 +58,16 @@ public class DerivedImageIcon extends LazyImageIcon {
|
||||
return sourceIcon;
|
||||
}
|
||||
|
||||
protected boolean sourceIconChanged() {
|
||||
if (sourceIcon instanceof GIcon gIcon) {
|
||||
if (cachedDelegate != gIcon.getDelegate()) {
|
||||
cachedDelegate = gIcon.getDelegate();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected ImageIcon createImageIcon() {
|
||||
Image image = createImage();
|
||||
String imageName = getFilename();
|
||||
|
@ -39,7 +39,7 @@ public abstract class LazyImageIcon extends ImageIcon implements FileBasedIcon {
|
||||
}
|
||||
|
||||
private synchronized void init() {
|
||||
if (!loaded) {
|
||||
if (!loaded || sourceIconChanged()) {
|
||||
loaded = true;
|
||||
ImageIcon imageIcon = createImageIcon();
|
||||
if (imageIcon == null) {
|
||||
@ -52,6 +52,10 @@ public abstract class LazyImageIcon extends ImageIcon implements FileBasedIcon {
|
||||
|
||||
protected abstract ImageIcon createImageIcon();
|
||||
|
||||
protected boolean sourceIconChanged() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFilename() {
|
||||
return getDescription();
|
||||
|
@ -44,13 +44,13 @@ public class GThemeTest extends AbstractGenericTest {
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
theme = Gui.getDefaultTheme();
|
||||
theme = new GTheme("TestTheme");
|
||||
new Font("Courier", Font.BOLD, 12);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetName() {
|
||||
assertEquals(Gui.getDefaultTheme().getName(), theme.getName());
|
||||
assertEquals("TestTheme", theme.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -97,7 +97,7 @@ public class GThemeTest extends AbstractGenericTest {
|
||||
|
||||
File file = createTempFile("themeTest", ".theme");
|
||||
|
||||
theme.saveToFile(file, false); // saveToFile returns new theme instance
|
||||
new ThemeWriter(theme).writeThemeToFile(file);
|
||||
theme = new ThemeReader(file).readTheme();
|
||||
|
||||
assertEquals("abc", theme.getName());
|
||||
|
@ -34,7 +34,7 @@ import generic.theme.builtin.*;
|
||||
import resources.ResourceManager;
|
||||
import resources.icons.UrlImageIcon;
|
||||
|
||||
public class GuiTest {
|
||||
public class ThemeManagerTest {
|
||||
|
||||
private Font FONT = new Font("Dialog", Font.PLAIN, 13);
|
||||
private Font SMALL_FONT = new Font("Dialog", Font.PLAIN, 4);
|
||||
@ -49,9 +49,11 @@ public class GuiTest {
|
||||
private GTheme NIMBUS_THEME = new NimbusTheme();
|
||||
private GTheme WINDOWS_THEME = new WindowsTheme();
|
||||
private GTheme MAC_THEME = new MacTheme();
|
||||
private ThemeManager themeManager;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
|
||||
themes = new HashSet<>();
|
||||
themes.add(METAL_THEME);
|
||||
themes.add(NIMBUS_THEME);
|
||||
@ -66,43 +68,8 @@ public class GuiTest {
|
||||
|
||||
darkDefaultValues.addColor(new ColorValue("color.test.bg", BLACK));
|
||||
darkDefaultValues.addColor(new ColorValue("color.test.fg", BLUE));
|
||||
themeManager = new DummyApplicationThemeManager();
|
||||
|
||||
Gui.setThemePreferenceManager(new ThemePreferenceManager() {
|
||||
|
||||
@Override
|
||||
public GTheme getTheme() {
|
||||
return new MetalTheme();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveThemeToPreferences(GTheme theme) {
|
||||
// do nothing
|
||||
}
|
||||
});
|
||||
|
||||
Gui.setPropertiesLoader(new ThemeFileLoader() {
|
||||
|
||||
@Override
|
||||
public void loadThemeDefaultFiles() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<GTheme> loadThemeFiles() {
|
||||
return new HashSet<>(themes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GThemeValueMap getDefaults() {
|
||||
return defaultValues;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GThemeValueMap getDarkDefaults() {
|
||||
return darkDefaultValues;
|
||||
}
|
||||
});
|
||||
Gui.initialize();
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -110,12 +77,11 @@ public class GuiTest {
|
||||
GColor gColor = new GColor("color.test.bg");
|
||||
|
||||
assertColor(WHITE, gColor);
|
||||
Gui.setTheme(new GTheme("Test", LafType.FLAT_DARK, true));
|
||||
themeManager.setTheme(new GTheme("Test", LafType.FLAT_DARK, true));
|
||||
assertEquals(BLACK, gColor);
|
||||
|
||||
Gui.setTheme(new GTheme("Test2"));
|
||||
themeManager.setTheme(new GTheme("Test2"));
|
||||
assertEquals(WHITE, gColor);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -126,25 +92,25 @@ public class GuiTest {
|
||||
theme.setColor("color.test.bg", GREEN);
|
||||
|
||||
assertColor(WHITE, gColor);
|
||||
Gui.setTheme(theme);
|
||||
themeManager.setTheme(theme);
|
||||
assertEquals(GREEN, gColor);
|
||||
|
||||
Gui.setTheme(new GTheme("Test2"));
|
||||
themeManager.setTheme(new GTheme("Test2"));
|
||||
assertEquals(WHITE, gColor);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThemeFontOverride() {
|
||||
assertEquals(FONT, Gui.getFont("font.test.foo"));
|
||||
assertEquals(FONT, themeManager.getFont("font.test.foo"));
|
||||
|
||||
GTheme theme = new GTheme("Test");
|
||||
theme.setFont("font.test.foo", SMALL_FONT);
|
||||
Gui.setTheme(theme);
|
||||
themeManager.setTheme(theme);
|
||||
|
||||
assertEquals(SMALL_FONT, Gui.getFont("font.test.foo"));
|
||||
assertEquals(SMALL_FONT, themeManager.getFont("font.test.foo"));
|
||||
|
||||
Gui.setTheme(new GTheme("Test2"));
|
||||
assertEquals(FONT, Gui.getFont("font.test.foo"));
|
||||
themeManager.setTheme(new GTheme("Test2"));
|
||||
assertEquals(FONT, themeManager.getFont("font.test.foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -155,10 +121,10 @@ public class GuiTest {
|
||||
theme.setIcon("icon.test.foo", ICON2);
|
||||
|
||||
assertIcon(ICON1, gIcon);
|
||||
Gui.setTheme(theme);
|
||||
themeManager.setTheme(theme);
|
||||
assertIcon(ICON2, gIcon);
|
||||
|
||||
Gui.setTheme(new GTheme("Test2"));
|
||||
themeManager.setTheme(new GTheme("Test2"));
|
||||
assertIcon(ICON1, gIcon);
|
||||
}
|
||||
|
||||
@ -168,7 +134,7 @@ public class GuiTest {
|
||||
assertColor(WHITE, gColor);
|
||||
|
||||
defaultValues.addColor(new ColorValue("color.test.bg", YELLOW));
|
||||
Gui.reloadApplicationDefaults();
|
||||
themeManager.reloadApplicationDefaults();
|
||||
assertEquals(YELLOW, gColor);
|
||||
}
|
||||
|
||||
@ -177,50 +143,50 @@ public class GuiTest {
|
||||
GColor gColor = new GColor("color.test.bg");
|
||||
assertColor(WHITE, gColor);
|
||||
|
||||
Gui.setColor("color.test.bg", PURPLE);
|
||||
themeManager.setColor("color.test.bg", PURPLE);
|
||||
assertColor(PURPLE, gColor);
|
||||
|
||||
Gui.restoreThemeValues();
|
||||
themeManager.restoreThemeValues();
|
||||
assertEquals(WHITE, gColor);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAllThemes() {
|
||||
assertEquals(themes, Gui.getAllThemes());
|
||||
assertEquals(themes, themeManager.getAllThemes());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddTheme() {
|
||||
GTheme newTheme = new GTheme("Test");
|
||||
|
||||
Set<GTheme> allThemes = Gui.getAllThemes();
|
||||
Set<GTheme> allThemes = themeManager.getAllThemes();
|
||||
assertEquals(themes.size(), allThemes.size());
|
||||
assertFalse(allThemes.contains(newTheme));
|
||||
|
||||
Gui.addTheme(newTheme);
|
||||
allThemes = Gui.getAllThemes();
|
||||
themeManager.addTheme(newTheme);
|
||||
allThemes = themeManager.getAllThemes();
|
||||
assertTrue(allThemes.contains(newTheme));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteTheme() {
|
||||
GTheme newTheme = new GTheme("Test");
|
||||
Set<GTheme> allThemes = Gui.getAllThemes();
|
||||
Set<GTheme> allThemes = themeManager.getAllThemes();
|
||||
assertFalse(allThemes.contains(newTheme));
|
||||
|
||||
Gui.addTheme(newTheme);
|
||||
allThemes = Gui.getAllThemes();
|
||||
themeManager.addTheme(newTheme);
|
||||
allThemes = themeManager.getAllThemes();
|
||||
assertTrue(allThemes.contains(newTheme));
|
||||
|
||||
Gui.deleteTheme(newTheme);
|
||||
allThemes = Gui.getAllThemes();
|
||||
themeManager.deleteTheme(newTheme);
|
||||
allThemes = themeManager.getAllThemes();
|
||||
assertFalse(allThemes.contains(newTheme));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSupportedThemes() {
|
||||
Set<GTheme> supportedThemes = Gui.getSupportedThemes();
|
||||
Set<GTheme> supportedThemes = themeManager.getSupportedThemes();
|
||||
// since we put mac specific and windows specific themes, they can't all be here
|
||||
// regardless of the current platform
|
||||
assertTrue(supportedThemes.size() < themes.size());
|
||||
@ -231,31 +197,31 @@ public class GuiTest {
|
||||
|
||||
@Test
|
||||
public void testGetLookAndFeelType() {
|
||||
LafType lookAndFeelType = Gui.getLookAndFeelType();
|
||||
LafType lookAndFeelType = themeManager.getLookAndFeelType();
|
||||
// in the test setup, we defaulted to the MetalLookAndFeel
|
||||
assertEquals(LafType.METAL, lookAndFeelType);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetActiveTheme() {
|
||||
GTheme activeTheme = Gui.getActiveTheme();
|
||||
GTheme activeTheme = themeManager.getActiveTheme();
|
||||
assertEquals(METAL_THEME, activeTheme);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetThemeByName() {
|
||||
GTheme theme = Gui.getTheme("Nimbus Theme");
|
||||
GTheme theme = themeManager.getTheme("Nimbus Theme");
|
||||
assertEquals(NIMBUS_THEME, theme);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAllValues() {
|
||||
GThemeValueMap allValues = Gui.getAllValues();
|
||||
GThemeValueMap allValues = themeManager.getCurrentValues();
|
||||
assertEquals(WHITE, allValues.getColor("color.test.bg").getRawValue());
|
||||
|
||||
Gui.setColor("color.test.bg", PURPLE);
|
||||
themeManager.setColor("color.test.bg", PURPLE);
|
||||
|
||||
allValues = Gui.getAllValues();
|
||||
allValues = themeManager.getCurrentValues();
|
||||
assertEquals(PURPLE, allValues.getColor("color.test.bg").getRawValue());
|
||||
|
||||
}
|
||||
@ -263,17 +229,17 @@ public class GuiTest {
|
||||
@Test
|
||||
public void testGetNonDefaultValues() {
|
||||
// should be empty if we haven't changed any themeValues
|
||||
GThemeValueMap nonDefaultValues = Gui.getNonDefaultValues();
|
||||
GThemeValueMap nonDefaultValues = themeManager.getNonDefaultValues();
|
||||
assertTrue(nonDefaultValues.isEmpty());
|
||||
|
||||
// change some values and see that they show up in the nonDefaultValues
|
||||
Gui.setColor("color.test.bg", RED);
|
||||
Gui.setFont("font.test.foo", SMALL_FONT);
|
||||
Gui.setIcon("icon.test.foo", ICON2);
|
||||
themeManager.setColor("color.test.bg", RED);
|
||||
themeManager.setFont("font.test.foo", SMALL_FONT);
|
||||
themeManager.setIcon("icon.test.foo", ICON2);
|
||||
// also add in a totally new value
|
||||
Gui.setColor("color.test.xxx", GREEN);
|
||||
themeManager.setColor("color.test.xxx", GREEN);
|
||||
|
||||
nonDefaultValues = Gui.getNonDefaultValues();
|
||||
nonDefaultValues = themeManager.getNonDefaultValues();
|
||||
assertEquals(4, nonDefaultValues.size());
|
||||
assertEquals(RED, nonDefaultValues.getColor("color.test.bg").getRawValue());
|
||||
assertEquals(GREEN, nonDefaultValues.getColor("color.test.xxx").getRawValue());
|
||||
@ -283,57 +249,57 @@ public class GuiTest {
|
||||
|
||||
@Test
|
||||
public void testGetColor() {
|
||||
assertEquals(WHITE, Gui.getColor("color.test.bg"));
|
||||
assertEquals(WHITE, themeManager.getColor("color.test.bg"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetFont() {
|
||||
assertEquals(FONT, Gui.getFont("font.test.foo"));
|
||||
assertEquals(FONT, themeManager.getFont("font.test.foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetIcon() {
|
||||
assertEquals(ICON1, Gui.getIcon("icon.test.foo"));
|
||||
assertEquals(ICON1, themeManager.getIcon("icon.test.foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetColorWithUnresolvedId() {
|
||||
assertEquals(CYAN, Gui.getColor("color.badid", false));
|
||||
assertEquals(CYAN, themeManager.getColor("color.badid"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetIconWithUnresolvedId() {
|
||||
assertEquals(ResourceManager.getDefaultIcon(), Gui.getIcon("icon.badid", false));
|
||||
assertEquals(ResourceManager.getDefaultIcon(), themeManager.getIcon("icon.badid"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetFontWithUnresolvedId() {
|
||||
assertEquals(Gui.DEFAULT_FONT, Gui.getFont("font.badid", false));
|
||||
assertEquals(ThemeManager.DEFAULT_FONT, themeManager.getFont("font.badid"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetGColorUiResource() {
|
||||
Color color = Gui.getGColorUiResource("color.test.bg");
|
||||
Color color = themeManager.getGColorUiResource("color.test.bg");
|
||||
assertTrue(color instanceof UIResource);
|
||||
|
||||
// make sure there is only one instance for an id;
|
||||
Color color2 = Gui.getGColorUiResource("color.test.bg");
|
||||
Color color2 = themeManager.getGColorUiResource("color.test.bg");
|
||||
assertTrue(color == color2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetGIconUiResource() {
|
||||
Icon icon = Gui.getGIconUiResource("icon.test.foo");
|
||||
Icon icon = themeManager.getGIconUiResource("icon.test.foo");
|
||||
assertTrue(icon instanceof UIResource);
|
||||
|
||||
// make sure there is only one instance for an id;
|
||||
Icon gIcon2 = Gui.getGIconUiResource("icon.test.foo");
|
||||
Icon gIcon2 = themeManager.getGIconUiResource("icon.test.foo");
|
||||
assertTrue(icon == gIcon2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetApplicationLightDefaults() {
|
||||
assertEquals(defaultValues, Gui.getApplicationLightDefaults());
|
||||
assertEquals(defaultValues, themeManager.getApplicationLightDefaults());
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -342,17 +308,17 @@ public class GuiTest {
|
||||
GThemeValueMap expected = new GThemeValueMap();
|
||||
expected.load(defaultValues);
|
||||
expected.load(darkDefaultValues);
|
||||
assertEquals(expected, Gui.getApplicationDarkDefaults());
|
||||
assertEquals(expected, themeManager.getApplicationDarkDefaults());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRegisterFont() {
|
||||
Gui.setFont(new FontValue("font.test", SMALL_FONT));
|
||||
themeManager.setFont(new FontValue("font.test", SMALL_FONT));
|
||||
JLabel label = new JLabel("Test");
|
||||
assertNotEquals(SMALL_FONT, label.getFont());
|
||||
Gui.registerFont(label, "font.test");
|
||||
themeManager.registerFont(label, "font.test");
|
||||
assertEquals(SMALL_FONT, label.getFont());
|
||||
Gui.setFont(new FontValue("font.test", FONT));
|
||||
themeManager.setFont(new FontValue("font.test", FONT));
|
||||
assertEquals(FONT, label.getFont());
|
||||
}
|
||||
|
||||
@ -369,4 +335,43 @@ public class GuiTest {
|
||||
fail("Icons don't match. Expected " + url + ", but got " + gUrl);
|
||||
}
|
||||
}
|
||||
|
||||
// ApplicationThemeManager that doesn't read in theme.properties files or preferences
|
||||
class DummyApplicationThemeManager extends ApplicationThemeManager {
|
||||
DummyApplicationThemeManager() {
|
||||
themePreferences = new ThemePreferences() {
|
||||
@Override
|
||||
public GTheme load() {
|
||||
return new MetalTheme();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(GTheme theme) {
|
||||
// do nothing
|
||||
}
|
||||
};
|
||||
themeFileLoader = new ThemeFileLoader() {
|
||||
@Override
|
||||
public void loadThemeDefaultFiles() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<GTheme> loadThemeFiles() {
|
||||
return new HashSet<>(themes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GThemeValueMap getDefaults() {
|
||||
return defaultValues;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GThemeValueMap getDarkDefaults() {
|
||||
return darkDefaultValues;
|
||||
}
|
||||
};
|
||||
doInitialize();
|
||||
}
|
||||
}
|
||||
}
|
@ -21,9 +21,11 @@ import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class WeakStoreTest {
|
||||
import generic.test.AbstractGenericTest;
|
||||
|
||||
public class WeakStoreTest extends AbstractGenericTest {
|
||||
@Test
|
||||
public void testStore() throws InterruptedException {
|
||||
public void testStore() {
|
||||
WeakStore<Foo> store = new WeakStore<>();
|
||||
store.add(new Foo("AAA"));
|
||||
store.add(new Foo("BBB"));
|
||||
@ -38,13 +40,10 @@ public class WeakStoreTest {
|
||||
assertEquals("CCC", values.get(2).getName());
|
||||
values = null;
|
||||
|
||||
for (int i = 0; i < 20; i++) {
|
||||
waitFor(() -> {
|
||||
System.gc();
|
||||
if (store.size() == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
assertEquals(0, store.size());
|
||||
return store.size() == 0;
|
||||
}, "Weak store values were never garbage collected");
|
||||
}
|
||||
|
||||
static class Foo {
|
||||
|
@ -17,6 +17,7 @@ package ghidra.service.graph;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Font;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
@ -24,32 +25,23 @@ import java.util.List;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import docking.FakeDockingTool;
|
||||
import docking.test.AbstractDockingTest;
|
||||
import generic.theme.*;
|
||||
import generic.theme.GThemeDefaults.Colors.Palette;
|
||||
import generic.theme.Gui;
|
||||
import ghidra.framework.options.Options;
|
||||
import ghidra.framework.options.ToolOptions;
|
||||
import ghidra.util.HelpLocation;
|
||||
|
||||
public class GraphDisplayOptionsTest extends AbstractDockingTest {
|
||||
public class GraphDisplayOptionsTest {
|
||||
|
||||
private GraphType graphType;
|
||||
private GraphDisplayOptions options;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
Gui.setColor("color.V1", Palette.BLACK);
|
||||
Gui.setColor("color.V2", Palette.BLACK);
|
||||
Gui.setColor("color.V3", Palette.BLACK);
|
||||
Gui.setColor("color.E1", Palette.BLACK);
|
||||
Gui.setColor("color.E2", Palette.BLACK);
|
||||
Gui.setColor("color.E3", Palette.BLACK);
|
||||
Gui.setColor("color.edge.default", Palette.BLACK);
|
||||
Gui.setColor("color.vertex.default", Palette.BLACK);
|
||||
Gui.setColor("color.edge.selected", Palette.BLACK);
|
||||
Gui.setColor("color.vertex.selected", Palette.BLACK);
|
||||
Gui.setFont("font.graph", new Font("monospaced", Font.PLAIN, 12));
|
||||
// create a dummy theme manager that defines values for use in this test
|
||||
DummyThemeManager themeManager = new DummyThemeManager();
|
||||
|
||||
// create a new graph definition and options using theme properties
|
||||
List<String> vertexTypes = Arrays.asList("V1", "V2", "V3");
|
||||
List<String> edgeTypes = Arrays.asList("E1", "E2", "E3");
|
||||
graphType = new GraphType("Test", "Test Description", vertexTypes, edgeTypes);
|
||||
@ -286,22 +278,44 @@ public class GraphDisplayOptionsTest extends AbstractDockingTest {
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChangingToolOptionsAffectsGraph() {
|
||||
FakeDockingTool tool = new FakeDockingTool();
|
||||
ToolOptions toolOptions = tool.getOptions("Graph");
|
||||
options.registerOptions(toolOptions, null);
|
||||
options.initializeFromOptions(tool);
|
||||
// Create a ThemeManager that it not fully initialized for speed. This class provides
|
||||
// fake property theme values.
|
||||
class DummyThemeManager extends StubThemeManager {
|
||||
DummyThemeManager() {
|
||||
installTestValues();
|
||||
installExpectedValues();
|
||||
installInGui();
|
||||
}
|
||||
|
||||
AttributedVertex vertex = new AttributedVertex("Foo");
|
||||
vertex.setVertexType("V1");
|
||||
assertEquals(Palette.BLACK.getRGB(), options.getVertexColor(vertex).getRGB());
|
||||
private void installExpectedValues() {
|
||||
setColor(new ColorValue("color.vertex.selected", Color.BLACK));
|
||||
setColor(new ColorValue("color.edge.selected", Color.BLACK));
|
||||
setColor(new ColorValue("color.graphdisplay.vertex.default", Color.BLACK));
|
||||
setColor(new ColorValue("color.graphdisplay.edge.default", Color.BLACK));
|
||||
setFont(new FontValue("font.graph", new Font("monospaced", Font.PLAIN, 12)));
|
||||
|
||||
Options graphDisplayOptions = toolOptions.getOptions(options.getRootOptionsName());
|
||||
Options vertexColorOptions = graphDisplayOptions.getOptions("Vertex Colors");
|
||||
vertexColorOptions.setColor("V1", Palette.GOLD);
|
||||
}
|
||||
|
||||
protected void installTestValues() {
|
||||
setColor(new ColorValue("color.V1", Color.BLACK));
|
||||
setColor(new ColorValue("color.V2", Color.BLACK));
|
||||
setColor(new ColorValue("color.V3", Color.BLACK));
|
||||
setColor(new ColorValue("color.E1", Color.BLACK));
|
||||
setColor(new ColorValue("color.E2", Color.BLACK));
|
||||
setColor(new ColorValue("color.E3", Color.BLACK));
|
||||
setColor(new ColorValue("color.edge.default", Color.BLACK));
|
||||
setColor(new ColorValue("color.vertex.default", Color.BLACK));
|
||||
setColor(new ColorValue("color.edge.selected", Color.BLACK));
|
||||
setColor(new ColorValue("color.vertex.selected", Color.BLACK));
|
||||
setColor(new ColorValue("color.graphdisplay.vertex.selected", Color.BLACK));
|
||||
setColor(new ColorValue("color.graphdisplay.edge.selected", Color.BLACK));
|
||||
setColor(new ColorValue("color.vertex.selected", Color.BLACK));
|
||||
setColor(new ColorValue("color.vertex.selected", Color.BLACK));
|
||||
|
||||
setFont(new FontValue("font.graph", new Font("monospaced", Font.PLAIN, 12)));
|
||||
setFont(
|
||||
new FontValue("font.graphdisplay.default", new Font("monospaced", Font.PLAIN, 12)));
|
||||
}
|
||||
|
||||
assertEquals(Palette.GOLD.getRGB(), options.getVertexColor(vertex).getRGB());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.*;
|
||||
|
||||
import generic.theme.Gui;
|
||||
import generic.theme.ApplicationThemeManager;
|
||||
import ghidra.framework.Application;
|
||||
import ghidra.framework.ApplicationConfiguration;
|
||||
import help.validator.*;
|
||||
@ -69,7 +69,7 @@ public class GHelpBuilder {
|
||||
ApplicationConfiguration config = new ApplicationConfiguration() {
|
||||
@Override
|
||||
protected void initializeApplication() {
|
||||
Gui.initialize();
|
||||
ApplicationThemeManager.initialize();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -25,7 +25,7 @@ import java.nio.file.Paths;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import generic.theme.Gui;
|
||||
import generic.theme.ApplicationThemeManager;
|
||||
import ghidra.GhidraTestApplicationLayout;
|
||||
import ghidra.framework.ApplicationConfiguration;
|
||||
import utility.application.ApplicationLayout;
|
||||
@ -101,7 +101,7 @@ public class HelpBuildUtilsTest extends AbstractHelpTest {
|
||||
|
||||
@Test
|
||||
public void testLocateReferences_Icons() throws URISyntaxException {
|
||||
Gui.initialize();
|
||||
ApplicationThemeManager.initialize();
|
||||
Path sourceFile = Paths.get(HTML_FILE_PATH);
|
||||
String reference = "Icons.REFRESH_ICON"; // see Icons class
|
||||
ImageLocation location = HelpBuildUtils.locateImageReference(sourceFile, reference);
|
||||
|
@ -18,7 +18,7 @@ package ghidra.app.plugin.gui;
|
||||
import docking.action.builder.ActionBuilder;
|
||||
import docking.theme.gui.ThemeDialog;
|
||||
import docking.theme.gui.ThemeUtils;
|
||||
import generic.theme.Gui;
|
||||
import generic.theme.ThemeManager;
|
||||
import ghidra.app.plugin.PluginCategoryNames;
|
||||
import ghidra.framework.main.ApplicationLevelOnlyPlugin;
|
||||
import ghidra.framework.main.UtilityPluginPackage;
|
||||
@ -38,8 +38,11 @@ import ghidra.util.HelpLocation;
|
||||
//@formatter:on
|
||||
public class ThemeManagerPlugin extends Plugin implements ApplicationLevelOnlyPlugin {
|
||||
|
||||
private ThemeManager themeManager;
|
||||
|
||||
public ThemeManagerPlugin(PluginTool tool) {
|
||||
super(tool);
|
||||
themeManager = ThemeManager.getInstance();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -52,35 +55,35 @@ public class ThemeManagerPlugin extends Plugin implements ApplicationLevelOnlyPl
|
||||
.menuPath("Edit", "Theme")
|
||||
.menuGroup(group, "1")
|
||||
.helpLocation(new HelpLocation("Theming", "Edit_Theme"))
|
||||
.onAction(e -> ThemeDialog.editTheme())
|
||||
.onAction(e -> ThemeDialog.editTheme(themeManager))
|
||||
.buildAndInstall(tool);
|
||||
|
||||
new ActionBuilder("Reset", owner)
|
||||
.menuPath("Edit", themeSubMenu, "Reset Theme Values")
|
||||
.menuGroup(group, "2")
|
||||
.helpLocation(new HelpLocation("Theming", "Reset_Theme_Values"))
|
||||
.onAction(e -> ThemeUtils.resetThemeToDefault())
|
||||
.onAction(e -> ThemeUtils.resetThemeToDefault(themeManager))
|
||||
.buildAndInstall(tool);
|
||||
|
||||
new ActionBuilder("Import Theme", owner)
|
||||
.menuPath("Edit", themeSubMenu, "Import...")
|
||||
.menuGroup(group, "3")
|
||||
.helpLocation(new HelpLocation("Theming", "Import_Theme"))
|
||||
.onAction(e -> ThemeUtils.importTheme())
|
||||
.onAction(e -> ThemeUtils.importTheme(themeManager))
|
||||
.buildAndInstall(tool);
|
||||
|
||||
new ActionBuilder("Export Theme", owner)
|
||||
.menuPath("Edit", themeSubMenu, "Export...")
|
||||
.menuGroup(group, "4")
|
||||
.helpLocation(new HelpLocation("Theming", "Export_Theme"))
|
||||
.onAction(e -> ThemeUtils.exportTheme())
|
||||
.onAction(e -> ThemeUtils.exportTheme(themeManager))
|
||||
.buildAndInstall(tool);
|
||||
|
||||
new ActionBuilder("Delete Theme", owner)
|
||||
.menuPath("Edit", themeSubMenu, "Delete...")
|
||||
.menuGroup(group, "5")
|
||||
.helpLocation(new HelpLocation("Theming", "Delete_Theme"))
|
||||
.onAction(e -> ThemeUtils.deleteTheme())
|
||||
.onAction(e -> ThemeUtils.deleteTheme(themeManager))
|
||||
.buildAndInstall(tool);
|
||||
|
||||
tool.setMenuGroup(new String[] { "Edit", themeSubMenu }, group, "2");
|
||||
@ -89,8 +92,8 @@ public class ThemeManagerPlugin extends Plugin implements ApplicationLevelOnlyPl
|
||||
|
||||
@Override
|
||||
protected boolean canClose() {
|
||||
if (Gui.hasThemeChanges()) {
|
||||
return ThemeUtils.askToSaveThemeChanges();
|
||||
if (themeManager.hasThemeChanges()) {
|
||||
return ThemeUtils.askToSaveThemeChanges(themeManager);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -26,13 +26,16 @@ import resources.ResourceManager;
|
||||
|
||||
public class ThemingScreenShots extends GhidraScreenShotGenerator {
|
||||
|
||||
private ThemeManager themeManager;
|
||||
|
||||
public ThemingScreenShots() {
|
||||
super();
|
||||
themeManager = ThemeManager.getInstance();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThemeDialog() {
|
||||
showDialogWithoutBlocking(tool, new ThemeDialog());
|
||||
showDialogWithoutBlocking(tool, new ThemeDialog(themeManager));
|
||||
captureDialog(1000, 500);
|
||||
}
|
||||
|
||||
@ -41,7 +44,7 @@ public class ThemingScreenShots extends GhidraScreenShotGenerator {
|
||||
ColorValueEditor editor = new ColorValueEditor(e -> {
|
||||
/**/});
|
||||
ColorValue value = new ColorValue("color.bg.test", Palette.BLUE);
|
||||
Gui.setColor(value);
|
||||
themeManager.setColor(value);
|
||||
editor.editValue(value);
|
||||
captureDialog();
|
||||
}
|
||||
@ -51,7 +54,7 @@ public class ThemingScreenShots extends GhidraScreenShotGenerator {
|
||||
FontValueEditor editor = new FontValueEditor(e -> {
|
||||
/**/});
|
||||
FontValue value = new FontValue("font.xyz", new Font("Monospaced", Font.BOLD, 14));
|
||||
Gui.setFont(value);
|
||||
themeManager.setFont(value);
|
||||
editor.editValue(value);
|
||||
captureDialog();
|
||||
}
|
||||
@ -61,7 +64,7 @@ public class ThemingScreenShots extends GhidraScreenShotGenerator {
|
||||
IconValueEditor editor = new IconValueEditor(e -> {
|
||||
/**/});
|
||||
IconValue value = new IconValue("icon.bomb", ResourceManager.getDefaultIcon());
|
||||
Gui.setIcon(value);
|
||||
themeManager.setIcon(value);
|
||||
editor.editValue(value);
|
||||
captureDialog();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user