GP-2934 cleaning up user interface for Theming
@ -33,7 +33,7 @@ public class PickProviderDialog extends DialogComponentProvider {
|
||||
private static String lastSelectedProviderDescription;
|
||||
|
||||
private List<GhidraScriptProvider> providers;
|
||||
private ListPanel listPanel;
|
||||
private ListPanel<GhidraScriptProvider> listPanel;
|
||||
private JComponent parent;
|
||||
private boolean wasCancelled;
|
||||
|
||||
@ -61,11 +61,12 @@ public class PickProviderDialog extends DialogComponentProvider {
|
||||
* @param testItems values to populate model with
|
||||
* @param defaultItem the default selection
|
||||
*/
|
||||
public PickProviderDialog(List<String> testItems, String defaultItem) {
|
||||
public PickProviderDialog(List<GhidraScriptProvider> testItems,
|
||||
GhidraScriptProvider defaultItem) {
|
||||
super("New Script: Type");
|
||||
|
||||
DefaultListModel<String> listModel = new DefaultListModel<>();
|
||||
for (String item : testItems) {
|
||||
DefaultListModel<GhidraScriptProvider> listModel = new DefaultListModel<>();
|
||||
for (GhidraScriptProvider item : testItems) {
|
||||
listModel.addElement(item);
|
||||
}
|
||||
|
||||
@ -143,8 +144,8 @@ public class PickProviderDialog extends DialogComponentProvider {
|
||||
close();
|
||||
}
|
||||
|
||||
private JPanel buildWorkPanel(DefaultListModel<?> listModel) {
|
||||
listPanel = new ListPanel();
|
||||
private JPanel buildWorkPanel(DefaultListModel<GhidraScriptProvider> listModel) {
|
||||
listPanel = new ListPanel<>();
|
||||
listPanel.setListModel(listModel);
|
||||
listPanel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
listPanel.setSelectedIndex(0);
|
||||
|
@ -42,7 +42,7 @@ public class SaveDialog extends DialogComponentProvider implements ListSelection
|
||||
private GhidraScriptProvider provider;
|
||||
|
||||
private List<ResourceFile> paths;
|
||||
private ListPanel listPanel;
|
||||
private ListPanel<ResourceFile> listPanel;
|
||||
private JTextField nameField;
|
||||
private boolean cancelled;
|
||||
|
||||
@ -113,7 +113,7 @@ public class SaveDialog extends DialogComponentProvider implements ListSelection
|
||||
listModel.addElement(dir);
|
||||
}
|
||||
|
||||
listPanel = new ListPanel();
|
||||
listPanel = new ListPanel<>();
|
||||
listPanel.setName("PATH_LIST");
|
||||
listPanel.setListModel(listModel);
|
||||
listPanel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
|
@ -19,12 +19,14 @@ src/main/help/help/topics/Theming/ThemingUserDocs.html||GHIDRA||||END|
|
||||
src/main/help/help/topics/Theming/images/ColorEditor.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/Theming/images/FontEditor.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/Theming/images/IconEditor.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/Theming/images/ThemeDialog.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/Theming/images/ThemeChooserDialog.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/Theming/images/ThemeEditorDialog.png||GHIDRA||||END|
|
||||
src/main/java/docking/dnd/package.html||GHIDRA||reviewed||END|
|
||||
src/main/java/docking/options/editor/package.html||GHIDRA||reviewed||END|
|
||||
src/main/java/docking/widgets/fieldpanel/package.html||GHIDRA||reviewed||END|
|
||||
src/main/java/docking/widgets/filechooser/package.html||GHIDRA||reviewed||END|
|
||||
src/main/java/docking/wizard/package.html||GHIDRA||reviewed||END|
|
||||
src/main/resources/images/Minus.png||GHIDRA||||END|
|
||||
src/main/resources/images/Plus.png||GHIDRA||reviewed||END|
|
||||
src/main/resources/images/StackFrameElement.png||GHIDRA||reviewed||END|
|
||||
src/main/resources/images/StackFrame_Red.png||GHIDRA||reviewed||END|
|
||||
@ -55,6 +57,7 @@ src/main/resources/images/expand.gif||GHIDRA||||END|
|
||||
src/main/resources/images/filter_off.png||GHIDRA||||END|
|
||||
src/main/resources/images/filter_on.png||GHIDRA||||END|
|
||||
src/main/resources/images/folder_add.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END|
|
||||
src/main/resources/images/font.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END|
|
||||
src/main/resources/images/hourglass.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END|
|
||||
src/main/resources/images/hourglass24_01.png||GHIDRA||reviewed||END|
|
||||
src/main/resources/images/hourglass24_02.png||GHIDRA||reviewed||END|
|
||||
|
@ -72,6 +72,11 @@ icon.window = application_xp.png
|
||||
icon.zoom.in = zoom_in.png
|
||||
icon.zoom.out = zoom_out.png
|
||||
|
||||
icon.theme.import = mail-receive.png
|
||||
icon.theme.export = mail-folder-outbox.png
|
||||
icon.theme.font.increment = font.png{Plus.png[size(8,8)][move(8,8)]}
|
||||
icon.theme.font.decrement = font.png{Minus.png[size(8,8)][move(8,8)]}
|
||||
|
||||
icon.docking.application.home = www_16.png
|
||||
icon.docking.application.16 = www_16.png
|
||||
icon.docking.application.128 = www_128.png
|
||||
|
@ -10,13 +10,14 @@
|
||||
</HEAD>
|
||||
|
||||
<BODY>
|
||||
<H1 align="center">Editing Themes</H1>
|
||||
<H1 align="center">Themes</H1>
|
||||
|
||||
<H2>Description</H2>
|
||||
<BlOCKQUOTE>
|
||||
|
||||
<P>The Theming feature allows users to customize the colors, fonts, and icons used throughout
|
||||
the application. The active theme determines the Java Look and Feel, whether the theme should use
|
||||
light or dark defaults, and any custom colors, fonts, and icons that override the default
|
||||
the application. The active theme determines the Java Look and Feel, whether the theme should
|
||||
use light or dark defaults, and any custom colors, fonts, and icons that override the default
|
||||
values. Users can can easily switch between any of the built-in themes or any saved themes found
|
||||
in their home application directory</P>
|
||||
|
||||
@ -26,22 +27,53 @@
|
||||
easily be modified using any text editor. Also, users can share themes by exporting them to a
|
||||
file that can be given to other users who can them import them into their application.</P>
|
||||
|
||||
<H2>Theme Dialog<A name="Edit_Theme"></A></H2>
|
||||
</BlOCKQUOTE>
|
||||
<H2>Theme Chooser Dialog<A name="Switch_Theme"></A></H2>
|
||||
<BlOCKQUOTE>
|
||||
<P>The Theme Chooser allows users to switch themes.
|
||||
|
||||
<P>The Theme Dialog is the primary means for creating, editing, and saving themes.</P>
|
||||
|
||||
<P align="center"><IMG alt="" src="images/ThemeDialog.png"><BR>
|
||||
<P align="center"><IMG alt="" src="images/ThemeChooserDialog.png"><BR>
|
||||
</P>
|
||||
|
||||
<P>The Theme Dialog consists of a theme drop-down and a tabbed set of tables that display the
|
||||
values for every color property, font property, and icon property defined by either the Java
|
||||
Look and Feel or the application. All application defined properties start with "color.",
|
||||
"font.", or "icon.", depending on whether the the property is a color, font, or icon
|
||||
respectively. All other properties are defined by the Java Look and Feel. This naming
|
||||
convention is not enforced, thus it is possible that 3rd-party Extensions may introduce
|
||||
property names that do not match this description. See
|
||||
the <A href="ThemingDeveloperDocs.html#Resource_Ids">Developer Documentation</A> for more details on the property ID
|
||||
format and naming conventions.
|
||||
<P>The Theme Chooser dialog displays a list of all the know themes, both built-in and custom.
|
||||
As you pick different themes, the application will switch to that theme. Press the "OK" button
|
||||
when the desired theme is selected. Pressing the "Cancel" button will restore the theme to
|
||||
the what it was when the dialog was invoked.</P>
|
||||
|
||||
</BlOCKQUOTE>
|
||||
|
||||
<H2>Theme Editor Dialog<A name="Edit_Theme"></A></H2>
|
||||
<BlOCKQUOTE>
|
||||
|
||||
<P>The Theme Editor Dialog is the primary means for creating and editing themes.</P>
|
||||
|
||||
<P align="center"><IMG alt="" src="images/ThemeEditorDialog.png"><BR>
|
||||
</P>
|
||||
|
||||
<P>The Theme Editor Dialog consists of a Look And Feel drop-down and a tabbed set of tables that
|
||||
display the values for every color property, font property, and icon property defined by either
|
||||
the Java look and feel or the application. The properties fall into one of the following
|
||||
groups</P>
|
||||
<UL>
|
||||
<LI>Application Properties - these are application defined properties. They all start with
|
||||
either "color.", "font.", or "icon.", depending on whether the property is a color, font,or
|
||||
icon respectively.</LI>
|
||||
<LI>System Properties - these are well defined Look and Feel concepts such as various
|
||||
standard background and foreground colors, border color, and fonts. These all start with
|
||||
"system." and their values are mapped to specific values for each Java Look and Feel.</LI>
|
||||
<LI>Look and Feel Properties - these are the properties defined by the current Java
|
||||
Look and Feel. Ghidra prepends these properties with prefixes that start with either
|
||||
"laf.color.", "laf.font"., or "laf.icon." For example, if the Look and Feel defines a property
|
||||
called "Button.background", it would appear in Ghidra as "laf.color.Button.background".</LI>
|
||||
<LI>Look and Feel Palette Properties - All the color and fonts used by the Look and Feel
|
||||
properties are grouped into either system property values or auto-generated palette
|
||||
properties, sso that groups of properties can be changed together.</LI>
|
||||
|
||||
</UL>
|
||||
|
||||
|
||||
<P>See the <A href="ThemingDeveloperDocs.html#Resource_Ids">Developer Documentation</A> for more
|
||||
details on the property ID format and naming conventions.
|
||||
</P>
|
||||
|
||||
<P>Each table entry shows the property ID string, the current value, the theme value,
|
||||
@ -63,8 +95,14 @@
|
||||
current theme is a built-in theme, you will first have to supply a new theme name. If the
|
||||
current theme is a not a built-in theme, you will have the option to overwrite the existing
|
||||
theme or supplying a new name to save it as a new theme.</P>
|
||||
|
||||
<H3>Theme Editor actions</H3>
|
||||
<UL>
|
||||
<LI><A name="Increment_Fonts"></A><IMG border="0" src="icon.theme.font.increment" > Increment All Fonts - Increases all fonts in the system by one.</LI>
|
||||
<LI><A name="Decrement_Fonts"></A><IMG border="0" src="icon.theme.font.decrement" > Decrement All Fonts - Decreases all fonts in the system by one.</LI>
|
||||
<LI><A name="Reload_Theme"></A><IMG border="0" src="icon.refresh" > Reload Theme - Restores all color, font, and icon values back to the original theme values.</LI>
|
||||
</UL>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<H3>Color Editor</H3>
|
||||
|
||||
<P>When you double-click on a color value, the Edit Color dialog appears.</P>
|
||||
@ -107,87 +145,95 @@
|
||||
<BLOCKQUOTE>
|
||||
<H3>Switching Themes</H3>
|
||||
|
||||
<P>To change the current theme, first bring up the <A href="#Edit_Theme">Theme Dialog</A>.
|
||||
The Theme Dialog can be invoked from the main application menu using the
|
||||
<B>Edit</B><IMG alt="" src="help/shared/arrow.gif" border="0"><B>Theme</B> menu. From the Theme
|
||||
Dialog you can select a theme from the combo box at the top.</P>
|
||||
<P>To change the current theme, invoke the
|
||||
<B>Edit<IMG alt="" src="help/shared/arrow.gif" border="0">Theme<IMG alt=""
|
||||
src="help/shared/arrow.gif" border="0">Change..." </b>menu option
|
||||
from the main project window. This will bring up the <A href="#Switch_Theme">
|
||||
Theme Chooser Dialog </A>which allows you to pick a new theme.</P>
|
||||
|
||||
<H3>Modifying Theme Values</H3>
|
||||
|
||||
<P>All the colors, fonts, and icons that have been registered with the theme API can be
|
||||
modified using the <A href="#Edit_Theme">Theme Dialog</A>. The Theme Dialog can be invoked
|
||||
from the main application menu using the
|
||||
<B>Edit</B><IMG alt="" src="help/shared/arrow.gif" border="0"><B>Theme</B> menu. Choose the
|
||||
modified using the <A href="#Edit_Theme">Theme Editor Dialog</A>. The Theme Editor Dialog
|
||||
can be invoked from the main application menu using the
|
||||
<B>Edit<IMG alt="" src="help/shared/arrow.gif" border="0">Theme<IMG alt=""
|
||||
src="help/shared/arrow.gif" border="0">Configure..." </b> menu. Choose the
|
||||
tab for the appropriate type and double-click on the ID column or Current Value column of the
|
||||
item you want to change. An editor for that type will appear.</P>
|
||||
|
||||
<H3>Creating New Themes<A NAME="New_Theme"></A></H3>
|
||||
|
||||
<P>To create a new theme, invoke <B>Edit<IMG alt="" src="help/shared/arrow.gif"
|
||||
border="0">Theme<IMG alt=""
|
||||
src="help/shared/arrow.gif" border="0">New..." </b>menu option. This will bring up a new
|
||||
dialog where you can enter the new theme name and select another theme as a starting point.
|
||||
When the <B>OK</B> button is pressed, the Theme Editor Dialog will appear, allowing you
|
||||
to begin making changes to theme values.</P>
|
||||
|
||||
|
||||
<H3>Reseting Theme Values<A NAME="Restore_Value"></A></H3>
|
||||
|
||||
<P>To reset an individual value back to its original theme value, from the
|
||||
main application menu invoke the <A href=
|
||||
"#Edit_Theme">Theme Dialog</A> using the <B>Edit</B> <IMG alt="" src="help/shared/arrow.gif"
|
||||
border="0"><B>Theme</B> menu. Choose the
|
||||
tab for the appropriate type and right-click on the row of the value you want to reset, then
|
||||
choose the <B>Restore Value</B> menu item.</P>
|
||||
<P>To reset an individual value back to its original theme value, invoke the <A href=
|
||||
"#Edit_Theme">Theme Editor Dialog</A>. Choose the tab for the appropriate type and
|
||||
right-click the one the row in the able you want to reset, then choose the
|
||||
<B>Restore Value</B> menu item from the popup menu.</P>
|
||||
|
||||
<H3>Reseting All Theme Values<A name="Reset_Theme_Values"></A></H3>
|
||||
|
||||
<P>To reset all values back to the original values established by the current theme, from the
|
||||
main application menu invoke
|
||||
the <B>Edit</B><IMG alt="" src="help/shared/arrow.gif" border="0"><B>Theme Actions</B> <IMG
|
||||
alt="" src="help/shared/arrow.gif" border="0"><B>Reset Theme Values</B> menu.</P>
|
||||
<P>To reset all values back to the original values established by the current theme,
|
||||
invoke the <A href= "#Edit_Theme">Theme Editor Dialog</A>. To activate this
|
||||
action, press the refresh button <IMG alt="" src="images/reload3.png" border="0"> in the top
|
||||
right corner of the Theme Editor dialog.
|
||||
|
||||
<H3>Saving Themes</H3>
|
||||
|
||||
<P>After making changes to one or more theme values, the <A href="#Edit_Theme">Theme
|
||||
Dialog's</A> <B>Save</B> button will be enabled. Pressing the <B>Save</B> button will give
|
||||
Editor Dialog's</A> <B>Save</B> button will be enabled. Pressing the <B>Save</B> button will give
|
||||
the user the option of creating a new theme or overwriting the current them (if the current
|
||||
theme is not a built-in theme). Also, users will have the option of saving a theme if they
|
||||
dismiss the Theme Dialog while there are changes to one or more theme values.</P>
|
||||
|
||||
<H3>Deleting Themes<A name="Delete_Theme"></A></H3>
|
||||
|
||||
<P>To delete a custom saved theme, from the main application menu invoke the
|
||||
<P>Only custom non-built-in themes can be deleted. To delete a custom theme, invoke the
|
||||
<B>Edit</B><IMG alt="" src=
|
||||
"help/shared/arrow.gif" border="0"><B>Theme Actions</B> <IMG alt="" src=
|
||||
"help/shared/arrow.gif" border="0"><B>Delete Theme...</B> menu. This will bring up a dialog
|
||||
"help/shared/arrow.gif" border="0"><B>Theme</B> <IMG alt="" src=
|
||||
"help/shared/arrow.gif" border="0"><B>Delete...</B> menu from the main application
|
||||
menu. This will bring up a dialog
|
||||
with a list of themes that can be deleted. Select the theme to delete and press the <B>Ok</B>
|
||||
button.</P>
|
||||
|
||||
<H3>Exporting Themes><A name="Export_Theme"></A></H3>
|
||||
<H3>Exporting Themes<A name="Export_Theme"></A></H3>
|
||||
|
||||
<P>To export a theme so that it can be shared with others, from the
|
||||
main application menu invoke the <B>Edit</B> <IMG alt=""
|
||||
src="help/shared/arrow.gif" border="0"><B>Theme Actions</B> <IMG alt="" src=
|
||||
"help/shared/arrow.gif" border="0"><B>Export Theme...</B> menu. You will first be asked if
|
||||
<P>To export a theme so that it can be shared with others, invoke the <B>Edit</B> <IMG alt=""
|
||||
src="help/shared/arrow.gif" border="0"><B>Theme</B> <IMG alt="" src=
|
||||
"help/shared/arrow.gif" border="0"><B>Export..</B> menu from the main application
|
||||
window. You will first be asked if
|
||||
you want to export as a regular theme file or as a Zip file. The Zip file option is useful if
|
||||
the current theme has icon values that are not included with standard application. In that case,
|
||||
the Zip file will include those non-standard icon files.</P>
|
||||
|
||||
<H3>Importing Themes<A name="Import_Theme"></A></H3>
|
||||
|
||||
<P>To import a theme, from the main application menu
|
||||
<P>To import a theme,
|
||||
invoke the <B>Edit</B> <IMG alt="" src="help/shared/arrow.gif" border=
|
||||
"0"><B>Theme Actions</B> <IMG alt="" src="help/shared/arrow.gif" border="0"><B>Import
|
||||
Theme...</B> menu. A file chooser dialog will appear allowing the user to choose a theme file
|
||||
"0"><B>Theme</B> <IMG alt="" src="help/shared/arrow.gif" border="0"><B>Import...</B>
|
||||
menu from the main application menu. A file chooser dialog will appear
|
||||
allowing the user to choose a theme file
|
||||
to import. The selected file can be either a standard theme file or a Zip file containing the
|
||||
theme file and any non-standard icon files defined by that theme.</P>
|
||||
|
||||
<H3>Reloading Default Values<A NAME="Reload_Ghidra_Defaults"></A></H3>
|
||||
|
||||
<P>This action causes Ghidra to reload all theme default values. It is really only useful
|
||||
for developers who are actively making changes to theme.properties files. To activate this
|
||||
action, press the refresh button <IMG alt="" src="images/reload3.png" border="0"> in the top
|
||||
right corner of the <A href="#Edit_Theme">Theme Dialog</A>.</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H2>Theme Property Names</H2>
|
||||
<BlOCKQUOTE>
|
||||
|
||||
<P>Theme Property Names (also referred to as IDs or keys) that are defined by the application
|
||||
use a common format to help make sorting and viewing properties more intuitive as to their use. See
|
||||
the <A href="ThemingDeveloperDocs.html#Resource_Ids">Developer Documentation</A> for more details on the property ID
|
||||
format and naming conventions.</P>
|
||||
|
||||
</BlOCKQUOTE>
|
||||
<H2>Theme Files</H2>
|
||||
<BLOCKQUOTE>
|
||||
|
||||
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 6.7 KiB After Width: | Height: | Size: 7.9 KiB |
After Width: | Height: | Size: 8.3 KiB |
Before Width: | Height: | Size: 46 KiB |
After Width: | Height: | Size: 47 KiB |
@ -311,9 +311,8 @@ public class SplashScreen extends JWindow {
|
||||
Gui.registerFont(statusLabel, FONT_ID);
|
||||
statusLabel.setFont(Gui.getFont(FONT_ID));
|
||||
|
||||
CompoundBorder border =
|
||||
BorderFactory.createCompoundBorder(BorderFactory.createLoweredBevelBorder(),
|
||||
BorderFactory.createEmptyBorder(0, 5, 2, 5));
|
||||
CompoundBorder border = BorderFactory.createCompoundBorder(
|
||||
BorderFactory.createLoweredBevelBorder(), BorderFactory.createEmptyBorder(0, 5, 2, 5));
|
||||
statusLabel.setBorder(border);
|
||||
statusLabel.setOpaque(true);
|
||||
statusLabel.setBackground(Colors.BACKGROUND);
|
||||
|
@ -199,6 +199,7 @@ public class ThemeColorTableModel extends GDynamicColumnTableModel<ColorValue, O
|
||||
@Override
|
||||
public Component getTableCellRendererComponent(GTableCellRenderingData data) {
|
||||
JLabel label = (JLabel) super.getTableCellRendererComponent(data);
|
||||
setFont(Gui.getFont("font.monospaced"));
|
||||
ResolvedColor resolved = (ResolvedColor) data.getValue();
|
||||
|
||||
String text = getValueText(resolved);
|
||||
|
@ -19,7 +19,6 @@ import java.awt.BorderLayout;
|
||||
import java.awt.Component;
|
||||
import java.awt.event.*;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
@ -35,12 +34,11 @@ import ghidra.util.*;
|
||||
/**
|
||||
* Primary dialog for editing Themes.
|
||||
*/
|
||||
public class ThemeDialog extends DialogComponentProvider {
|
||||
private static ThemeDialog INSTANCE;
|
||||
public class ThemeEditorDialog extends DialogComponentProvider {
|
||||
private static ThemeEditorDialog INSTANCE;
|
||||
|
||||
private JButton saveButton;
|
||||
private JButton restoreButton;
|
||||
private GhidraComboBox<String> combo;
|
||||
private GhidraComboBox<LafType> combo;
|
||||
private ItemListener comboListener = this::themeComboChanged;
|
||||
private ThemeListener listener = new DialogThemeListener();
|
||||
private JTabbedPane tabbedPane;
|
||||
@ -53,14 +51,13 @@ public class ThemeDialog extends DialogComponentProvider {
|
||||
|
||||
private GThemeValuesCache valuesCache;
|
||||
|
||||
public ThemeDialog(ThemeManager themeManager) {
|
||||
super("Theme Dialog", false);
|
||||
public ThemeEditorDialog(ThemeManager themeManager) {
|
||||
super("Configure Theme: " + themeManager.getActiveTheme().getName(), false);
|
||||
this.themeManager = themeManager;
|
||||
addWorkPanel(createMainPanel());
|
||||
|
||||
addDismissButton();
|
||||
addButton(createSaveButton());
|
||||
addButton(createRestoreButton());
|
||||
|
||||
setPreferredSize(1100, 500);
|
||||
setRememberSize(false);
|
||||
@ -71,10 +68,30 @@ public class ThemeDialog extends DialogComponentProvider {
|
||||
}
|
||||
|
||||
private void createActions() {
|
||||
DockingAction reloadDefaultsAction = new ActionBuilder("Reload Theme Defaults", getTitle())
|
||||
|
||||
DockingAction incrementFontsAction = new ActionBuilder("Increment All Fonts", getTitle())
|
||||
.toolBarIcon(new GIcon("icon.theme.font.increment"))
|
||||
.description("Increases all font sizes by 1")
|
||||
.helpLocation(new HelpLocation("Theming", "Increment_Fonts"))
|
||||
.onAction(e -> adjustFonts(1))
|
||||
.build();
|
||||
addAction(incrementFontsAction);
|
||||
|
||||
DockingAction decrementFontsAction = new ActionBuilder("Decrement All Fonts", getTitle())
|
||||
.toolBarIcon(new GIcon("icon.theme.font.decrement"))
|
||||
.toolBarGroup("A")
|
||||
.description("Decreases all font sizes by 1")
|
||||
.helpLocation(new HelpLocation("Theming", "Decrement_Fonts"))
|
||||
.onAction(e -> adjustFonts(-1))
|
||||
.build();
|
||||
addAction(decrementFontsAction);
|
||||
|
||||
DockingAction reloadDefaultsAction = new ActionBuilder("Restore Theme Values", getTitle())
|
||||
.toolBarIcon(new GIcon("icon.refresh"))
|
||||
.helpLocation(new HelpLocation("Theming", "Reload_Ghidra_Defaults"))
|
||||
.onAction(e -> reloadDefaultsCallback())
|
||||
.toolBarGroup("B")
|
||||
.description("Reloads default values and restores theme to its original values.")
|
||||
.helpLocation(new HelpLocation("Theming", "Reload_Theme"))
|
||||
.onAction(e -> restoreCallback())
|
||||
.build();
|
||||
addAction(reloadDefaultsAction);
|
||||
|
||||
@ -89,10 +106,13 @@ public class ThemeDialog extends DialogComponentProvider {
|
||||
addAction(resetValueAction);
|
||||
}
|
||||
|
||||
private void adjustFonts(int amount) {
|
||||
themeManager.adjustFonts(amount);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dismissCallback() {
|
||||
if (handleChanges()) {
|
||||
INSTANCE = null;
|
||||
close();
|
||||
}
|
||||
}
|
||||
@ -118,7 +138,7 @@ public class ThemeDialog extends DialogComponentProvider {
|
||||
|
||||
private void restoreCallback() {
|
||||
if (themeManager.hasThemeChanges()) {
|
||||
int result = OptionDialog.showYesNoDialog(null, "Discard Theme Changes?",
|
||||
int result = OptionDialog.showYesNoDialog(null, "Restore Theme Values?",
|
||||
"This will discard all of your theme changes. Continue?");
|
||||
if (result != OptionDialog.YES_OPTION) {
|
||||
return;
|
||||
@ -127,23 +147,11 @@ public class ThemeDialog extends DialogComponentProvider {
|
||||
themeManager.restoreThemeValues();
|
||||
}
|
||||
|
||||
private void reloadDefaultsCallback() {
|
||||
if (themeManager.hasThemeChanges()) {
|
||||
int result = OptionDialog.showYesNoDialog(null, "Reload Default Theme Values?",
|
||||
"This will discard all of your theme changes. Continue?");
|
||||
if (result != OptionDialog.YES_OPTION) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
themeManager.reloadApplicationDefaults();
|
||||
}
|
||||
|
||||
private void reset() {
|
||||
colorTable.reloadAll();
|
||||
fontTable.reloadAll();
|
||||
iconTable.reloadAll();
|
||||
updateButtons();
|
||||
updateCombo();
|
||||
}
|
||||
|
||||
private void themeComboChanged(ItemEvent e) {
|
||||
@ -152,16 +160,11 @@ public class ThemeDialog extends DialogComponentProvider {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ThemeUtils.askToSaveThemeChanges(themeManager)) {
|
||||
Swing.runLater(() -> updateCombo());
|
||||
return;
|
||||
}
|
||||
|
||||
String themeName = (String) e.getItem();
|
||||
LafType lafType = (LafType) e.getItem();
|
||||
Swing.runLater(() -> {
|
||||
GTheme theme = themeManager.getTheme(themeName);
|
||||
themeManager.setTheme(theme);
|
||||
if (theme.getLookAndFeelType() == LafType.GTK) {
|
||||
|
||||
themeManager.setLookAndFeel(lafType, lafType.usesDarkDefaults());
|
||||
if (lafType == LafType.GTK) {
|
||||
setStatusText(
|
||||
"Warning - Themes using the GTK LookAndFeel do not support changing java " +
|
||||
"component colors, fonts or icons.",
|
||||
@ -179,7 +182,6 @@ public class ThemeDialog extends DialogComponentProvider {
|
||||
private void updateButtons() {
|
||||
boolean hasChanges = themeManager.hasThemeChanges();
|
||||
saveButton.setEnabled(hasChanges);
|
||||
restoreButton.setEnabled(hasChanges);
|
||||
}
|
||||
|
||||
private JComponent createMainPanel() {
|
||||
@ -199,34 +201,26 @@ public class ThemeDialog extends DialogComponentProvider {
|
||||
return panel;
|
||||
}
|
||||
|
||||
private void updateCombo() {
|
||||
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(themeManager.getActiveTheme().getName());
|
||||
combo.addItemListener(comboListener);
|
||||
}
|
||||
|
||||
private Component buildThemeCombo() {
|
||||
JPanel panel = new JPanel();
|
||||
Set<GTheme> supportedThemes = themeManager.getSupportedThemes();
|
||||
List<String> themeNames =
|
||||
supportedThemes.stream().map(t -> t.getName()).collect(Collectors.toList());
|
||||
Collections.sort(themeNames);
|
||||
List<LafType> lafs = getSupportedLookAndFeels();
|
||||
|
||||
combo = new GhidraComboBox<>(themeNames);
|
||||
combo.setSelectedItem(themeManager.getActiveTheme().getName());
|
||||
combo = new GhidraComboBox<>(lafs);
|
||||
combo.setSelectedItem(themeManager.getActiveTheme().getLookAndFeelType());
|
||||
combo.addItemListener(comboListener);
|
||||
|
||||
panel.add(new JLabel("Theme: "), BorderLayout.WEST);
|
||||
panel.add(new JLabel("Look And Feel: "), BorderLayout.WEST);
|
||||
panel.add(combo);
|
||||
panel.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 10));
|
||||
return panel;
|
||||
}
|
||||
|
||||
private List<LafType> getSupportedLookAndFeels() {
|
||||
LafType[] lafTypes = LafType.values();
|
||||
Comparator<LafType> comparator = (a, b) -> a.getName().compareTo(b.getName());
|
||||
return Arrays.stream(lafTypes).filter(laf -> laf.isSupported()).sorted(comparator).toList();
|
||||
}
|
||||
|
||||
private Component buildTabedTables() {
|
||||
tabbedPane = new JTabbedPane();
|
||||
|
||||
@ -243,15 +237,6 @@ public class ThemeDialog extends DialogComponentProvider {
|
||||
return tabbedPane;
|
||||
}
|
||||
|
||||
private JButton createRestoreButton() {
|
||||
restoreButton = new JButton("Restore");
|
||||
restoreButton.setMnemonic('R');
|
||||
restoreButton.setName("Restore");
|
||||
restoreButton.addActionListener(e -> restoreCallback());
|
||||
restoreButton.setToolTipText("Restores all previous values to current theme");
|
||||
return restoreButton;
|
||||
}
|
||||
|
||||
private JButton createSaveButton() {
|
||||
saveButton = new JButton("Save");
|
||||
saveButton.setMnemonic('S');
|
||||
@ -270,15 +255,19 @@ public class ThemeDialog extends DialogComponentProvider {
|
||||
INSTANCE.toFront();
|
||||
return;
|
||||
}
|
||||
INSTANCE = new ThemeDialog(themeManager);
|
||||
INSTANCE = new ThemeEditorDialog(themeManager);
|
||||
DockingWindowManager.showDialog(INSTANCE);
|
||||
}
|
||||
|
||||
public static ThemeEditorDialog getRunningInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
Gui.removeThemeListener(listener);
|
||||
super.close();
|
||||
INSTANCE = null;
|
||||
}
|
||||
|
||||
@Override
|
@ -46,15 +46,17 @@ public class ThemeUtils {
|
||||
*/
|
||||
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 to save your changes?");
|
||||
int result = OptionDialog.showOptionDialog(null, "Save Theme Changes?",
|
||||
"You have made changes to the current theme.\n" +
|
||||
" Do you want to save or abort your changes",
|
||||
"Save", "Abort");
|
||||
if (result == OptionDialog.CANCEL_OPTION) {
|
||||
return false;
|
||||
}
|
||||
if (result == OptionDialog.YES_OPTION) {
|
||||
if (result == OptionDialog.OPTION_ONE) {
|
||||
return saveThemeChanges(themeManager);
|
||||
}
|
||||
themeManager.reloadApplicationDefaults();
|
||||
themeManager.restoreThemeValues();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -70,6 +72,9 @@ public class ThemeUtils {
|
||||
String name = activeTheme.getName();
|
||||
|
||||
while (!canSaveToName(themeManager, name)) {
|
||||
if (shouldCancelSave(themeManager, name)) {
|
||||
return false;
|
||||
}
|
||||
name = getNameFromUser(name);
|
||||
if (name == null) {
|
||||
return false;
|
||||
@ -78,14 +83,21 @@ public class ThemeUtils {
|
||||
return saveCurrentValues(themeManager, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the theme to the default, handling the case where the current theme has changes.
|
||||
* @param themeManager the theme manager
|
||||
*/
|
||||
public static void resetThemeToDefault(ThemeManager themeManager) {
|
||||
if (askToSaveThemeChanges(themeManager)) {
|
||||
themeManager.setTheme(ThemeManager.getDefaultTheme());
|
||||
private static boolean shouldCancelSave(ThemeManager themeManager, String name) {
|
||||
if (name.isBlank()) {
|
||||
int result = OptionDialog.showOptionNoCancelDialog(null, "Invalid Theme Name",
|
||||
"The theme name can't be blank!\nDo you want to enter a new name?", "Yes", "Cancel",
|
||||
OptionDialog.QUESTION_MESSAGE);
|
||||
return result != OptionDialog.OPTION_ONE;
|
||||
}
|
||||
GTheme existing = themeManager.getTheme(name);
|
||||
if (existing instanceof DiscoverableGTheme) {
|
||||
int result = OptionDialog.showOptionNoCancelDialog(null, "Unmodifiable Theme",
|
||||
"The current theme is unmodifiable!\nDo you want to save as a new theme?", "Yes",
|
||||
"Cancel", OptionDialog.QUESTION_MESSAGE);
|
||||
return result != OptionDialog.OPTION_ONE;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -199,9 +211,12 @@ public class ThemeUtils {
|
||||
return inputDialog.getValue();
|
||||
}
|
||||
|
||||
private static boolean canSaveToName(ThemeManager themeManager, String name) {
|
||||
public static Boolean canSaveToName(ThemeManager themeManager, String name) {
|
||||
if (name.isBlank()) {
|
||||
return false;
|
||||
}
|
||||
GTheme existing = themeManager.getTheme(name);
|
||||
// if no theme exists with that name, then we are save to save it
|
||||
// if no theme exists with that name, then we are safe to save it
|
||||
if (existing == null) {
|
||||
return true;
|
||||
}
|
||||
@ -225,9 +240,19 @@ public class ThemeUtils {
|
||||
if (!file.exists()) {
|
||||
Msg.info(ThemeUtils.class, "Saving theme to " + file);
|
||||
}
|
||||
LafType lafType = activeTheme.getLookAndFeelType();
|
||||
boolean useDarkDefaults = activeTheme.useDarkDefaults();
|
||||
|
||||
GTheme newTheme = new GTheme(file, themeName, activeTheme.getLookAndFeelType(),
|
||||
activeTheme.useDarkDefaults());
|
||||
LafType currentLafType = themeManager.getLookAndFeelType();
|
||||
|
||||
// if the user has change the lookAndFeel from the current active theme, then
|
||||
// save it as the lookAndFeel for the newly saved theme
|
||||
if (currentLafType != activeTheme.getLookAndFeelType()) {
|
||||
lafType = currentLafType;
|
||||
useDarkDefaults = lafType.usesDarkDefaults();
|
||||
}
|
||||
|
||||
GTheme newTheme = new GTheme(file, themeName, lafType, useDarkDefaults);
|
||||
newTheme.load(themeManager.getNonDefaultValues());
|
||||
try {
|
||||
newTheme.save();
|
||||
@ -242,7 +267,7 @@ public class ThemeUtils {
|
||||
return true;
|
||||
}
|
||||
|
||||
private static File getSaveFile(String themeName) {
|
||||
public static File getSaveFile(String themeName) {
|
||||
File dir = Application.getUserSettingsDirectory();
|
||||
File themeDir = new File(dir, ThemeManager.THEME_DIR);
|
||||
if (!themeDir.exists()) {
|
||||
|
@ -24,8 +24,9 @@ import javax.swing.event.ListSelectionListener;
|
||||
|
||||
/**
|
||||
* This class provides a panel that contains a JList component.
|
||||
* @param <T> The type for the items in this list
|
||||
*/
|
||||
public class ListPanel extends JPanel {
|
||||
public class ListPanel<T> extends JPanel {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final String DEFAULT_WARNING = "You must first select an item from the list.";
|
||||
@ -33,7 +34,7 @@ public class ListPanel extends JPanel {
|
||||
private ActionListener doubleClickActionListener;
|
||||
private MouseListener mouseListener;
|
||||
private JScrollPane scrollpane;
|
||||
private JList list;
|
||||
private JList<T> list;
|
||||
|
||||
/**
|
||||
* Constructs a new ListPanel.
|
||||
@ -45,7 +46,7 @@ public class ListPanel extends JPanel {
|
||||
|
||||
// Enforce a minimum size, for some amount of consistency. The magic value was
|
||||
// discovered via trial-and-error.
|
||||
list = new JList() {
|
||||
list = new JList<>() {
|
||||
@Override
|
||||
public Dimension getPreferredSize() {
|
||||
Dimension d = super.getPreferredSize();
|
||||
@ -105,6 +106,22 @@ public class ListPanel extends JPanel {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a {@link ListSelectionListener}
|
||||
* @param listener the listener to add
|
||||
*/
|
||||
public void addListSelectionListener(ListSelectionListener listener) {
|
||||
list.addListSelectionListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a {@link ListSelectionListener}
|
||||
* @param listener the listener to remove
|
||||
*/
|
||||
public void removeListSelectionListener(ListSelectionListener listener) {
|
||||
list.removeListSelectionListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if no list items are selected.
|
||||
* @return true if no list items are selected.
|
||||
@ -117,7 +134,7 @@ public class ListPanel extends JPanel {
|
||||
* Returns the first selected value in the list or null if nothing is selected.
|
||||
* @return the first selected value in the list or null if nothing is selected.
|
||||
*/
|
||||
public Object getSelectedValue() {
|
||||
public T getSelectedValue() {
|
||||
return list.getSelectedValue();
|
||||
}
|
||||
|
||||
@ -141,7 +158,7 @@ public class ListPanel extends JPanel {
|
||||
* Selects the item.
|
||||
* @param item the item to select
|
||||
*/
|
||||
public void setSelectedValue(Object item) {
|
||||
public void setSelectedValue(T item) {
|
||||
list.setSelectedValue(item, true);
|
||||
}
|
||||
|
||||
@ -149,15 +166,15 @@ public class ListPanel extends JPanel {
|
||||
* Returns an array of all the selected items.
|
||||
* @return an array of all the selected items.
|
||||
*/
|
||||
public Object[] getSelectedValues() {
|
||||
return list.getSelectedValues();
|
||||
public java.util.List<T> getSelectedValues() {
|
||||
return list.getSelectedValuesList();
|
||||
}
|
||||
|
||||
/**
|
||||
* replaces the list contents with the new list.
|
||||
* @param dataList the new list for the contents.
|
||||
*/
|
||||
public void refreshList(Object[] dataList) {
|
||||
public void refreshList(T[] dataList) {
|
||||
list.setListData(dataList);
|
||||
list.clearSelection();
|
||||
}
|
||||
@ -166,7 +183,7 @@ public class ListPanel extends JPanel {
|
||||
* Sets the list data
|
||||
* @param data the data
|
||||
*/
|
||||
public void setListData(Object[] data) {
|
||||
public void setListData(T[] data) {
|
||||
list.setListData(data);
|
||||
}
|
||||
|
||||
@ -174,7 +191,7 @@ public class ListPanel extends JPanel {
|
||||
* Sets a list model for the internal list to use.
|
||||
* @param listModel the list model to use.
|
||||
*/
|
||||
public void setListModel(ListModel listModel) {
|
||||
public void setListModel(ListModel<T> listModel) {
|
||||
list.setModel(listModel);
|
||||
list.clearSelection();
|
||||
}
|
||||
@ -183,7 +200,7 @@ public class ListPanel extends JPanel {
|
||||
* Get the list model for the list.
|
||||
* @return the list model for the list.
|
||||
*/
|
||||
public ListModel getListModel() {
|
||||
public ListModel<T> getListModel() {
|
||||
return (list.getModel());
|
||||
}
|
||||
|
||||
@ -191,7 +208,7 @@ public class ListPanel extends JPanel {
|
||||
* Return the JList component.
|
||||
* @return the JList component.
|
||||
*/
|
||||
public JList getList() {
|
||||
public JList<T> getList() {
|
||||
return list;
|
||||
}
|
||||
|
||||
@ -199,7 +216,7 @@ public class ListPanel extends JPanel {
|
||||
* Get the cell renderer for the list.
|
||||
* @param r the cell renderer to use.
|
||||
*/
|
||||
public void setCellRenderer(ListCellRenderer r) {
|
||||
public void setCellRenderer(ListCellRenderer<T> r) {
|
||||
list.setCellRenderer(r);
|
||||
}
|
||||
|
||||
@ -284,7 +301,7 @@ public class ListPanel extends JPanel {
|
||||
final JFrame frame = new JFrame("ListPanel");
|
||||
frame.getContentPane().setLayout(new GridLayout(1, 1));
|
||||
|
||||
final ListPanel lbp = new ListPanel();
|
||||
final ListPanel<String> lbp = new ListPanel<>();
|
||||
final DefaultListModel<String> listModel = new DefaultListModel<>();
|
||||
frame.getContentPane().add(lbp);
|
||||
frame.addWindowListener(new WindowAdapter() {
|
||||
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 567 B After Width: | Height: | Size: 567 B |
@ -107,7 +107,13 @@ public class ThemeUtilsTest extends AbstractDockingTest {
|
||||
OptionDialog dialog = waitForDialogComponent(OptionDialog.class);
|
||||
assertNotNull(dialog);
|
||||
assertEquals("Save Theme Changes?", dialog.getTitle());
|
||||
pressButtonByText(dialog, "Save");
|
||||
|
||||
dialog = waitForDialogComponent(OptionDialog.class);
|
||||
assertNotNull(dialog);
|
||||
assertEquals("Unmodifiable Theme", dialog.getTitle());
|
||||
pressButtonByText(dialog, "Yes");
|
||||
|
||||
InputDialog inputDialog = waitForDialogComponent(InputDialog.class);
|
||||
assertNotNull(inputDialog);
|
||||
runSwing(() -> inputDialog.setValue("Joe"));
|
||||
@ -131,7 +137,7 @@ public class ThemeUtilsTest extends AbstractDockingTest {
|
||||
OptionDialog dialog = waitForDialogComponent(OptionDialog.class);
|
||||
assertNotNull(dialog);
|
||||
assertEquals("Save Theme Changes?", dialog.getTitle());
|
||||
pressButtonByText(dialog, "No");
|
||||
pressButtonByText(dialog, "Abort");
|
||||
waitForSwing();
|
||||
assertEquals("Bob", themeManager.getActiveTheme().getName());
|
||||
}
|
||||
|
@ -71,16 +71,12 @@ public class ApplicationThemeManager extends ThemeManager {
|
||||
setTheme(themePreferences.load());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reloadApplicationDefaults() {
|
||||
applicationDefaults = getApplicationDefaults();
|
||||
buildCurrentValues();
|
||||
lookAndFeelManager.resetAll(javaDefaults);
|
||||
notifyThemeChanged(new AllValuesChangedThemeEvent(false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreThemeValues() {
|
||||
if (activeLafType != activeTheme.getLookAndFeelType()) {
|
||||
setLookAndFeel(activeTheme.getLookAndFeelType(), activeTheme.useDarkDefaults());
|
||||
}
|
||||
applicationDefaults = getApplicationDefaults();
|
||||
buildCurrentValues();
|
||||
lookAndFeelManager.resetAll(javaDefaults);
|
||||
notifyThemeChanged(new AllValuesChangedThemeEvent(false));
|
||||
@ -126,21 +122,43 @@ public class ApplicationThemeManager extends ThemeManager {
|
||||
public void setTheme(GTheme theme) {
|
||||
if (theme.hasSupportedLookAndFeel()) {
|
||||
activeTheme = theme;
|
||||
LafType lafType = theme.getLookAndFeelType();
|
||||
activeLafType = theme.getLookAndFeelType();
|
||||
useDarkDefaults = theme.useDarkDefaults();
|
||||
|
||||
cleanUiDefaults(); // clear out any values previous themes may have installed
|
||||
lookAndFeelManager = lafType.getLookAndFeelManager(this);
|
||||
lookAndFeelManager = activeLafType.getLookAndFeelManager(this);
|
||||
try {
|
||||
lookAndFeelManager.installLookAndFeel();
|
||||
notifyThemeChanged(new AllValuesChangedThemeEvent(true));
|
||||
}
|
||||
catch (Exception e) {
|
||||
Msg.error(this, "Error setting LookAndFeel: " + lafType.getName(), e);
|
||||
Msg.error(this, "Error setting Look and Feel: " + activeLafType.getName(), e);
|
||||
}
|
||||
themePreferences.save(theme);
|
||||
}
|
||||
currentValues.checkForUnresolvedReferences();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLookAndFeel(LafType lafType, boolean useDarkDefaults) {
|
||||
if (!lafType.isSupported()) {
|
||||
Msg.error(this, "Attempted to set unsupported Look and Feel: " + lafType);
|
||||
return;
|
||||
}
|
||||
this.activeLafType = lafType;
|
||||
this.useDarkDefaults = useDarkDefaults;
|
||||
|
||||
cleanUiDefaults();
|
||||
lookAndFeelManager = lafType.getLookAndFeelManager(this);
|
||||
try {
|
||||
lookAndFeelManager.installLookAndFeel();
|
||||
notifyThemeChanged(new AllValuesChangedThemeEvent(true));
|
||||
}
|
||||
catch (Exception e) {
|
||||
Msg.error(this, "Error setting Look and Feel: " + lafType.getName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTheme(GTheme newTheme) {
|
||||
loadThemes();
|
||||
@ -166,14 +184,15 @@ public class ApplicationThemeManager extends ThemeManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<GTheme> getSupportedThemes() {
|
||||
public List<GTheme> getSupportedThemes() {
|
||||
loadThemes();
|
||||
Set<GTheme> supported = new HashSet<>();
|
||||
List<GTheme> supported = new ArrayList<>();
|
||||
for (GTheme theme : allThemes) {
|
||||
if (theme.hasSupportedLookAndFeel()) {
|
||||
supported.add(theme);
|
||||
}
|
||||
}
|
||||
Collections.sort(supported, (t1, t2) -> t1.getName().compareTo(t2.getName()));
|
||||
return supported;
|
||||
}
|
||||
|
||||
@ -262,7 +281,13 @@ public class ApplicationThemeManager extends ThemeManager {
|
||||
|
||||
@Override
|
||||
public boolean hasThemeChanges() {
|
||||
return !changedValuesMap.isEmpty();
|
||||
if (!changedValuesMap.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
if (lookAndFeelManager.getLookAndFeelType() != activeTheme.getLookAndFeelType()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -23,12 +23,17 @@ import ghidra.util.classfinder.ExtensionPoint;
|
||||
public abstract class DiscoverableGTheme extends GTheme implements ExtensionPoint {
|
||||
static final String CLASS_PREFIX = "Class:";
|
||||
|
||||
protected DiscoverableGTheme(String name, LafType lookAndFeel, boolean useDarkDefaults) {
|
||||
super(name, lookAndFeel, useDarkDefaults);
|
||||
protected DiscoverableGTheme(String name, LafType lookAndFeel) {
|
||||
super(name, lookAndFeel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getThemeLocater() {
|
||||
return CLASS_PREFIX + getClass().getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReadOnly() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -47,7 +47,7 @@ public class GTheme extends GThemeValueMap {
|
||||
* @param name the name for this GTheme
|
||||
*/
|
||||
public GTheme(String name) {
|
||||
this(name, LafType.getDefaultLookAndFeel(), false);
|
||||
this(name, LafType.getDefaultLookAndFeel());
|
||||
|
||||
}
|
||||
|
||||
@ -55,23 +55,22 @@ public class GTheme extends GThemeValueMap {
|
||||
* Creates a new empty GTheme with the given name, {@link LookAndFeel}, and whether or not to
|
||||
* use dark defaults.
|
||||
* @param name the name for the new GTheme
|
||||
* @param lookAndFeel the look and feel type used by this theme
|
||||
* @param useDarkDefaults determines whether or
|
||||
* @param lafType the look and feel type used by this theme
|
||||
*/
|
||||
public GTheme(String name, LafType lookAndFeel, boolean useDarkDefaults) {
|
||||
this(null, name, lookAndFeel, useDarkDefaults);
|
||||
public GTheme(String name, LafType lafType) {
|
||||
this(null, name, lafType, lafType.usesDarkDefaults());
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for creating a GTheme with an associated File.
|
||||
* @param file the file that this theme will save to
|
||||
* @param name the name of the new theme
|
||||
* @param lookAndFeel the {@link LafType} for the new theme
|
||||
* @param lafType the {@link LafType} for the new theme
|
||||
* @param useDarkDefaults true if this new theme uses dark defaults
|
||||
*/
|
||||
public GTheme(File file, String name, LafType lookAndFeel, boolean useDarkDefaults) {
|
||||
public GTheme(File file, String name, LafType lafType, boolean useDarkDefaults) {
|
||||
this.name = name;
|
||||
this.lookAndFeel = lookAndFeel;
|
||||
this.lookAndFeel = lafType;
|
||||
this.useDarkDefaults = useDarkDefaults;
|
||||
this.file = file;
|
||||
}
|
||||
@ -217,6 +216,14 @@ public class GTheme extends GThemeValueMap {
|
||||
writer.writeThemeToFile(file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this theme can not be changed
|
||||
* @return true if this theme can not be changed
|
||||
*/
|
||||
public boolean isReadOnly() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
|
@ -33,15 +33,21 @@ public enum LafType {
|
||||
GTK("GTK+"),
|
||||
MOTIF("CDE/Motif"),
|
||||
FLAT_LIGHT("Flat Light"),
|
||||
FLAT_DARK("Flat Dark"),
|
||||
FLAT_DARK("Flat Dark", true),
|
||||
WINDOWS("Windows"),
|
||||
WINDOWS_CLASSIC("Windows Classic"),
|
||||
MAC("Mac OS X");
|
||||
|
||||
private String name;
|
||||
private boolean usesDarkDefaults;
|
||||
|
||||
private LafType(String name) {
|
||||
this(name, false);
|
||||
}
|
||||
|
||||
private LafType(String name, boolean usesDarkDefaults) {
|
||||
this.name = name;
|
||||
this.usesDarkDefaults = usesDarkDefaults;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -52,6 +58,16 @@ public enum LafType {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the LookAndFeel represented by this LafType uses application dark
|
||||
* default values.
|
||||
* @return true if the LookAndFeel represented by this LafType uses application dark
|
||||
* default values.
|
||||
*/
|
||||
public boolean usesDarkDefaults() {
|
||||
return usesDarkDefaults;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the LafType for the given name or null if the given name does not match any types
|
||||
* @param name the name to search a LafType for.
|
||||
@ -135,4 +151,9 @@ public enum LafType {
|
||||
return NIMBUS;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getName();
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ import static ghidra.util.WebColors.*;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.swing.plaf.ComponentUI;
|
||||
@ -60,11 +61,6 @@ public class StubThemeManager extends ThemeManager {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reloadApplicationDefaults() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreThemeValues() {
|
||||
throw new UnsupportedOperationException();
|
||||
@ -121,7 +117,7 @@ public class StubThemeManager extends ThemeManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<GTheme> getSupportedThemes() {
|
||||
public List<GTheme> getSupportedThemes() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
|
@ -16,6 +16,7 @@
|
||||
package generic.theme;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.swing.Icon;
|
||||
@ -61,9 +62,13 @@ public abstract class ThemeManager {
|
||||
static final Font DEFAULT_FONT = new Font("Dialog", Font.PLAIN, 12);
|
||||
static final Color DEFAULT_COLOR = Color.CYAN;
|
||||
|
||||
private static final int MIN_FONT_SIZE = 3;
|
||||
|
||||
protected static ThemeManager INSTANCE;
|
||||
|
||||
protected GTheme activeTheme = getDefaultTheme();
|
||||
protected LafType activeLafType = activeTheme.getLookAndFeelType();
|
||||
protected boolean useDarkDefaults = activeTheme.useDarkDefaults();
|
||||
|
||||
protected GThemeValueMap javaDefaults = new GThemeValueMap();
|
||||
protected GThemeValueMap currentValues = new GThemeValueMap();
|
||||
@ -100,7 +105,7 @@ public abstract class ThemeManager {
|
||||
|
||||
map.load(javaDefaults);
|
||||
map.load(applicationDefaults.getLightValues());
|
||||
if (activeTheme.useDarkDefaults()) {
|
||||
if (useDarkDefaults) {
|
||||
map.load(applicationDefaults.getDarkValues());
|
||||
}
|
||||
map.load(applicationDefaults.getLookAndFeelValues(getLookAndFeelType()));
|
||||
@ -108,13 +113,6 @@ public abstract class ThemeManager {
|
||||
currentValues = map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads the defaults from all the discoverable theme.property files.
|
||||
*/
|
||||
public void reloadApplicationDefaults() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
@ -191,6 +189,19 @@ public abstract class ThemeManager {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current {@link LookAndFeel}. This is used by theme editors to allow users to
|
||||
* see the effects of changing LookAndFeels when configuring a theme. Setting this different
|
||||
* from the activeTheme's LookAndFeel setting means the the current theme is in an unsaved
|
||||
* state and causes the {@link #hasThemeChanges()} method to return true.
|
||||
* @param lafType the {@link LafType} to set the LookAndFeel to
|
||||
* @param useDarkDefaults true if the application should used dark defaults with this
|
||||
* LookAndFeel
|
||||
*/
|
||||
public void setLookAndFeel(LafType lafType, boolean useDarkDefaults) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given theme to set of all themes.
|
||||
* @param newTheme the theme to add
|
||||
@ -220,7 +231,7 @@ public abstract class ThemeManager {
|
||||
* 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 Set<GTheme> getSupportedThemes() {
|
||||
public List<GTheme> getSupportedThemes() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@ -237,7 +248,7 @@ public abstract class ThemeManager {
|
||||
* @return the {@link LafType} for the currently active {@link LookAndFeel}
|
||||
*/
|
||||
public LafType getLookAndFeelType() {
|
||||
return activeTheme.getLookAndFeelType();
|
||||
return activeLafType;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -268,7 +279,7 @@ public abstract class ThemeManager {
|
||||
GThemeValueMap map = new GThemeValueMap();
|
||||
map.load(javaDefaults);
|
||||
map.load(applicationDefaults.getLightValues());
|
||||
if (activeTheme.useDarkDefaults()) {
|
||||
if (useDarkDefaults) {
|
||||
map.load(applicationDefaults.getDarkValues());
|
||||
}
|
||||
map.load(applicationDefaults.getLookAndFeelValues(getLookAndFeelType()));
|
||||
@ -439,7 +450,7 @@ public abstract class ThemeManager {
|
||||
public GThemeValueMap getApplicationOverrides() {
|
||||
GThemeValueMap currentDefaults = new GThemeValueMap();
|
||||
currentDefaults.load(applicationDefaults.getLightValues());
|
||||
if (activeTheme.useDarkDefaults()) {
|
||||
if (useDarkDefaults) {
|
||||
currentDefaults.load(applicationDefaults.getDarkValues());
|
||||
}
|
||||
currentDefaults.load(applicationDefaults.getLookAndFeelValues(getLookAndFeelType()));
|
||||
@ -454,7 +465,7 @@ public abstract class ThemeManager {
|
||||
public GThemeValueMap getDefaults() {
|
||||
GThemeValueMap currentDefaults = new GThemeValueMap(javaDefaults);
|
||||
currentDefaults.load(applicationDefaults.getLightValues());
|
||||
if (activeTheme.useDarkDefaults()) {
|
||||
if (useDarkDefaults) {
|
||||
currentDefaults.load(applicationDefaults.getDarkValues());
|
||||
}
|
||||
currentDefaults.load(applicationDefaults.getLookAndFeelValues(getLookAndFeelType()));
|
||||
@ -545,7 +556,7 @@ public abstract class ThemeManager {
|
||||
* @return true if the current theme use dark default values.
|
||||
*/
|
||||
public boolean isDarkTheme() {
|
||||
return activeTheme.useDarkDefaults();
|
||||
return useDarkDefaults;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -581,4 +592,21 @@ public abstract class ThemeManager {
|
||||
Msg.error(this, message, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjust the size of all fonts by the given amount.
|
||||
* @param amount the number to add to the current font size;
|
||||
*/
|
||||
public void adjustFonts(int amount) {
|
||||
List<FontValue> fonts = currentValues.getFonts();
|
||||
for (FontValue fontValue : fonts) {
|
||||
Font directFont = fontValue.getRawValue();
|
||||
if (directFont == null) {
|
||||
continue; // indirect fonts will be handled when its referenced font is handled
|
||||
}
|
||||
int currentSize = directFont.getSize();
|
||||
int newSize = Math.max(MIN_FONT_SIZE, currentSize += amount);
|
||||
setFont(fontValue.getId(), directFont.deriveFont((float) newSize));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ import generic.theme.LafType;
|
||||
public class CDEMotifTheme extends DiscoverableGTheme {
|
||||
|
||||
public CDEMotifTheme() {
|
||||
super("Motif Theme", LafType.MOTIF, false);
|
||||
super("Motif Theme", LafType.MOTIF);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -25,6 +25,6 @@ import generic.theme.LafType;
|
||||
*/
|
||||
public class FlatDarkTheme extends DiscoverableGTheme {
|
||||
public FlatDarkTheme() {
|
||||
super("Flat Dark Theme", LafType.FLAT_DARK, true);
|
||||
super("Flat Dark Theme", LafType.FLAT_DARK);
|
||||
}
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ import generic.theme.LafType;
|
||||
public class FlatLightTheme extends DiscoverableGTheme {
|
||||
|
||||
public FlatLightTheme() {
|
||||
super("Flat Light Theme", LafType.FLAT_LIGHT, false);
|
||||
super("Flat Light Theme", LafType.FLAT_LIGHT);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ import generic.theme.LafType;
|
||||
public class GTKTheme extends DiscoverableGTheme {
|
||||
|
||||
public GTKTheme() {
|
||||
super("GTK+ Theme", LafType.GTK, false);
|
||||
super("GTK+ Theme", LafType.GTK);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -27,6 +27,6 @@ import generic.theme.LafType;
|
||||
public class MacTheme extends DiscoverableGTheme {
|
||||
|
||||
public MacTheme() {
|
||||
super("Mac OS X Theme", LafType.MAC, false);
|
||||
super("Mac OS X Theme", LafType.MAC);
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ import generic.theme.LafType;
|
||||
public class MetalTheme extends DiscoverableGTheme {
|
||||
|
||||
public MetalTheme() {
|
||||
super("Metal Theme", LafType.METAL, false);
|
||||
super("Metal Theme", LafType.METAL);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ import generic.theme.LafType;
|
||||
public class NimbusTheme extends DiscoverableGTheme {
|
||||
|
||||
public NimbusTheme() {
|
||||
super("Nimbus Theme", LafType.NIMBUS, false);
|
||||
super("Nimbus Theme", LafType.NIMBUS);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -27,6 +27,6 @@ import generic.theme.LafType;
|
||||
public class WindowsClassicTheme extends DiscoverableGTheme {
|
||||
|
||||
public WindowsClassicTheme() {
|
||||
super("Windows Classic Theme", LafType.WINDOWS_CLASSIC, false);
|
||||
super("Windows Classic Theme", LafType.WINDOWS_CLASSIC);
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,6 @@ import generic.theme.LafType;
|
||||
public class WindowsTheme extends DiscoverableGTheme {
|
||||
|
||||
public WindowsTheme() {
|
||||
super("Windows Theme", LafType.WINDOWS, false);
|
||||
super("Windows Theme", LafType.WINDOWS);
|
||||
}
|
||||
}
|
||||
|
@ -48,7 +48,6 @@ public class FlatDarkUiDefaultsMapper extends FlatUiDefaultsMapper {
|
||||
// our view background color so that they look like normal editable widgets
|
||||
//
|
||||
overrideColor("ComboBox.background", BG_VIEW_ID);
|
||||
overrideColor("ComboBox.background", BG_VIEW_ID);
|
||||
overrideColor("EditorPane.background", BG_VIEW_ID);
|
||||
overrideColor("FormattedTextField.background", BG_VIEW_ID);
|
||||
overrideColor("List.background", BG_VIEW_ID);
|
||||
@ -60,9 +59,9 @@ public class FlatDarkUiDefaultsMapper extends FlatUiDefaultsMapper {
|
||||
overrideColor("Tree.background", BG_VIEW_ID);
|
||||
overrideColor("Tree.textBackground", BG_VIEW_ID);
|
||||
overrideColor("TextArea.background", BG_VIEW_ID);
|
||||
overrideColor("TextArea.foreground", BG_VIEW_ID);
|
||||
overrideColor("TextArea.foreground", FG_VIEW_ID);
|
||||
overrideColor("TextPane.background", BG_VIEW_ID);
|
||||
overrideColor("TextPane.foreground", BG_VIEW_ID);
|
||||
overrideColor("TextPane.foreground", FG_VIEW_ID);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -82,7 +82,7 @@ public class ApplicationThemeManagerTest {
|
||||
GColor gColor = new GColor("color.test.bg");
|
||||
|
||||
assertColor(WHITE, gColor);
|
||||
themeManager.setTheme(new GTheme("Test", LafType.FLAT_DARK, true));
|
||||
themeManager.setTheme(new GTheme("Test", LafType.FLAT_DARK));
|
||||
assertEquals(BLACK, gColor);
|
||||
|
||||
themeManager.setTheme(new GTheme("Test2"));
|
||||
@ -139,7 +139,7 @@ public class ApplicationThemeManagerTest {
|
||||
assertColor(WHITE, gColor);
|
||||
|
||||
defaultValues.addColor(new ColorValue("color.test.bg", YELLOW));
|
||||
themeManager.reloadApplicationDefaults();
|
||||
themeManager.restoreThemeValues();
|
||||
assertEquals(YELLOW, gColor);
|
||||
}
|
||||
|
||||
@ -191,7 +191,7 @@ public class ApplicationThemeManagerTest {
|
||||
|
||||
@Test
|
||||
public void testGetSupportedThemes() {
|
||||
Set<GTheme> supportedThemes = themeManager.getSupportedThemes();
|
||||
List<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());
|
||||
|
@ -0,0 +1,149 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.gui;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
|
||||
import docking.DialogComponentProvider;
|
||||
import docking.theme.gui.ThemeUtils;
|
||||
import docking.widgets.combobox.GhidraComboBox;
|
||||
import generic.theme.GTheme;
|
||||
import generic.theme.ThemeManager;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.layout.PairLayout;
|
||||
|
||||
public class CreateThemeDialog extends DialogComponentProvider {
|
||||
|
||||
private JTextField nameField;
|
||||
private ThemeManager themeManager;
|
||||
private GhidraComboBox<GTheme> combo;
|
||||
private GTheme newTheme;
|
||||
|
||||
protected CreateThemeDialog(ThemeManager themeManager) {
|
||||
super("Create Theme");
|
||||
this.themeManager = themeManager;
|
||||
|
||||
addWorkPanel(buildMainPanel());
|
||||
addOKButton();
|
||||
addCancelButton();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void okCallback() {
|
||||
String themeName = nameField.getText().trim();
|
||||
File file = ThemeUtils.getSaveFile(themeName);
|
||||
GTheme baseTheme = (GTheme) combo.getSelectedItem();
|
||||
newTheme = new GTheme(file, themeName, baseTheme.getLookAndFeelType(),
|
||||
baseTheme.useDarkDefaults());
|
||||
newTheme.load(baseTheme);
|
||||
try {
|
||||
newTheme.save();
|
||||
}
|
||||
catch (IOException e) {
|
||||
Msg.showError(ThemeUtils.class, null, "I/O Error",
|
||||
"Error writing theme file: " + newTheme.getFile().getAbsolutePath(), e);
|
||||
newTheme = null;
|
||||
}
|
||||
close();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void cancelCallback() {
|
||||
close();
|
||||
}
|
||||
|
||||
private JComponent buildMainPanel() {
|
||||
JPanel panel = new JPanel(new PairLayout(10, 10));
|
||||
panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
|
||||
|
||||
nameField = createNameField();
|
||||
combo = buildThemeCombo();
|
||||
|
||||
panel.add(new JLabel("New Theme Name: "));
|
||||
panel.add(nameField);
|
||||
panel.add(new JLabel("Base Theme: "));
|
||||
panel.add(combo);
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
private JTextField createNameField() {
|
||||
JTextField jTextField = new JTextField(20);
|
||||
jTextField.getDocument().addDocumentListener(new DocumentListener() {
|
||||
@Override
|
||||
public void removeUpdate(DocumentEvent e) {
|
||||
updateOk();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertUpdate(DocumentEvent e) {
|
||||
updateOk();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changedUpdate(DocumentEvent e) {
|
||||
updateOk();
|
||||
}
|
||||
|
||||
});
|
||||
return jTextField;
|
||||
}
|
||||
|
||||
private void updateOk() {
|
||||
String name = nameField.getText().trim();
|
||||
setOkEnabled(isValidThemeName(name));
|
||||
}
|
||||
|
||||
private boolean isValidThemeName(String name) {
|
||||
if (name.isBlank()) {
|
||||
setStatusText("You must enter a theme name!");
|
||||
return false;
|
||||
}
|
||||
GTheme existing = themeManager.getTheme(name);
|
||||
// if no theme exists with that name, then we are safe to save it
|
||||
if (existing != null) {
|
||||
setStatusText("Theme already exists with that name!");
|
||||
return false;
|
||||
}
|
||||
clearStatusText();
|
||||
return true;
|
||||
}
|
||||
|
||||
private GhidraComboBox<GTheme> buildThemeCombo() {
|
||||
List<GTheme> supportedThemes = themeManager.getSupportedThemes();
|
||||
|
||||
GhidraComboBox<GTheme> ghidraComboBox = new GhidraComboBox<>(supportedThemes);
|
||||
ghidraComboBox.setSelectedItem(themeManager.getActiveTheme());
|
||||
return ghidraComboBox;
|
||||
}
|
||||
|
||||
public GTheme getNewTheme(PluginTool tool, String suggestedName) {
|
||||
if (suggestedName != null) {
|
||||
nameField.setText(suggestedName);
|
||||
nameField.selectAll();
|
||||
}
|
||||
updateOk();
|
||||
tool.showDialog(this);
|
||||
return newTheme;
|
||||
}
|
||||
}
|
@ -0,0 +1,106 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.gui;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.util.*;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import docking.DialogComponentProvider;
|
||||
import docking.widgets.list.ListPanel;
|
||||
import generic.theme.GTheme;
|
||||
import generic.theme.ThemeManager;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.Swing;
|
||||
|
||||
public class ThemeChooserDialog extends DialogComponentProvider {
|
||||
|
||||
private ThemeManager themeManager;
|
||||
private ListPanel<GTheme> listPanel;
|
||||
private GTheme originalTheme;
|
||||
|
||||
public ThemeChooserDialog(ThemeManager themeManager) {
|
||||
super("Change Theme");
|
||||
this.themeManager = themeManager;
|
||||
originalTheme = themeManager.getActiveTheme();
|
||||
addWorkPanel(buildMainPanel());
|
||||
addOKButton();
|
||||
addCancelButton();
|
||||
setRememberSize(false);
|
||||
setHelpLocation(new HelpLocation("Theming", "Switch_Theme"));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void okCallback() {
|
||||
close();
|
||||
}
|
||||
|
||||
protected void cancelCallback() {
|
||||
GTheme activeTheme = themeManager.getActiveTheme();
|
||||
if (activeTheme != originalTheme) {
|
||||
themeManager.setTheme(originalTheme);
|
||||
}
|
||||
close();
|
||||
}
|
||||
|
||||
private JComponent buildMainPanel() {
|
||||
JPanel panel = new JPanel(new BorderLayout());
|
||||
panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 0, 10));
|
||||
ThemeListModel model = new ThemeListModel();
|
||||
|
||||
listPanel = new ListPanel<>();
|
||||
listPanel.setListModel(model);
|
||||
listPanel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
GTheme activeTheme = themeManager.getActiveTheme();
|
||||
listPanel.setSelectedValue(activeTheme);
|
||||
listPanel.addListSelectionListener(e -> selectionChanged());
|
||||
panel.add(listPanel);
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
private void selectionChanged() {
|
||||
GTheme selectedValue = listPanel.getSelectedValue();
|
||||
if (selectedValue == null) {
|
||||
return;
|
||||
}
|
||||
GTheme activeTheme = themeManager.getActiveTheme();
|
||||
if (selectedValue != activeTheme) {
|
||||
Swing.runLater(() -> themeManager.setTheme(selectedValue));
|
||||
}
|
||||
}
|
||||
|
||||
private class ThemeListModel extends AbstractListModel<GTheme> {
|
||||
private List<GTheme> allThemes;
|
||||
|
||||
ThemeListModel() {
|
||||
allThemes = new ArrayList<>(themeManager.getSupportedThemes());
|
||||
Collections.sort(allThemes, (t1, t2) -> t1.getName().compareTo(t2.getName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSize() {
|
||||
return allThemes.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public GTheme getElementAt(int index) {
|
||||
return allThemes.get(index);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -16,8 +16,9 @@
|
||||
package ghidra.app.plugin.gui;
|
||||
|
||||
import docking.action.builder.ActionBuilder;
|
||||
import docking.theme.gui.ThemeDialog;
|
||||
import docking.theme.gui.ThemeEditorDialog;
|
||||
import docking.theme.gui.ThemeUtils;
|
||||
import generic.theme.GTheme;
|
||||
import generic.theme.ThemeManager;
|
||||
import ghidra.app.plugin.PluginCategoryNames;
|
||||
import ghidra.framework.main.ApplicationLevelOnlyPlugin;
|
||||
@ -48,46 +49,73 @@ public class ThemeManagerPlugin extends Plugin implements ApplicationLevelOnlyPl
|
||||
@Override
|
||||
protected void init() {
|
||||
String owner = getName();
|
||||
String themeSubMenu = "Theme Actions";
|
||||
|
||||
String group = "theme";
|
||||
new ActionBuilder("Edit Theme", owner)
|
||||
.menuPath("Edit", "Theme")
|
||||
|
||||
new ActionBuilder("Switch Theme", owner).menuPath("Edit", "Theme", "Switch...")
|
||||
.menuGroup(group, "1")
|
||||
.helpLocation(new HelpLocation("Theming", "Edit_Theme"))
|
||||
.onAction(e -> ThemeDialog.editTheme(themeManager))
|
||||
.helpLocation(new HelpLocation("Theming", "Switch_Theme"))
|
||||
.onAction(e -> switchTheme())
|
||||
.buildAndInstall(tool);
|
||||
|
||||
new ActionBuilder("Reset", owner)
|
||||
.menuPath("Edit", themeSubMenu, "Reset Theme Values")
|
||||
new ActionBuilder("Configure", owner).menuPath("Edit", "Theme", "Configure...")
|
||||
.menuGroup(group, "2")
|
||||
.helpLocation(new HelpLocation("Theming", "Reset_Theme_Values"))
|
||||
.onAction(e -> ThemeUtils.resetThemeToDefault(themeManager))
|
||||
.helpLocation(new HelpLocation("Theming", "Edit_Theme"))
|
||||
.onAction(e -> configure())
|
||||
.buildAndInstall(tool);
|
||||
|
||||
new ActionBuilder("Import Theme", owner)
|
||||
.menuPath("Edit", themeSubMenu, "Import...")
|
||||
new ActionBuilder("New Theme", owner).menuPath("Edit", "Theme", "New...")
|
||||
.menuGroup(group, "3")
|
||||
.helpLocation(new HelpLocation("Theming", "New_Theme"))
|
||||
.onAction(e -> createNewTheme())
|
||||
.buildAndInstall(tool);
|
||||
|
||||
new ActionBuilder("Import Theme", owner).menuPath("Edit", "Theme", "Import...")
|
||||
.menuGroup(group, "4")
|
||||
.helpLocation(new HelpLocation("Theming", "Import_Theme"))
|
||||
.onAction(e -> ThemeUtils.importTheme(themeManager))
|
||||
.buildAndInstall(tool);
|
||||
|
||||
new ActionBuilder("Export Theme", owner)
|
||||
.menuPath("Edit", themeSubMenu, "Export...")
|
||||
.menuGroup(group, "4")
|
||||
new ActionBuilder("Export Theme", owner).menuPath("Edit", "Theme", "Export...")
|
||||
.menuGroup(group, "5")
|
||||
.helpLocation(new HelpLocation("Theming", "Export_Theme"))
|
||||
.onAction(e -> ThemeUtils.exportTheme(themeManager))
|
||||
.buildAndInstall(tool);
|
||||
|
||||
new ActionBuilder("Delete Theme", owner)
|
||||
.menuPath("Edit", themeSubMenu, "Delete...")
|
||||
.menuGroup(group, "5")
|
||||
new ActionBuilder("Delete Theme", owner).menuPath("Edit", "Theme", "Delete...")
|
||||
.menuGroup(group, "6")
|
||||
.helpLocation(new HelpLocation("Theming", "Delete_Theme"))
|
||||
.onAction(e -> ThemeUtils.deleteTheme(themeManager))
|
||||
.buildAndInstall(tool);
|
||||
|
||||
tool.setMenuGroup(new String[] { "Edit", themeSubMenu }, group, "2");
|
||||
}
|
||||
|
||||
private void switchTheme() {
|
||||
ThemeChooserDialog dialog = new ThemeChooserDialog(themeManager);
|
||||
tool.showDialog(dialog);
|
||||
}
|
||||
|
||||
private void createNewTheme() {
|
||||
if (!ThemeUtils.askToSaveThemeChanges(themeManager)) {
|
||||
return; // user cancelled
|
||||
}
|
||||
|
||||
CreateThemeDialog dialog = new CreateThemeDialog(themeManager);
|
||||
GTheme newTheme = dialog.getNewTheme(tool, "New Theme");
|
||||
if (newTheme != null) {
|
||||
themeManager.addTheme(newTheme);
|
||||
themeManager.setTheme(newTheme);
|
||||
configure();
|
||||
}
|
||||
}
|
||||
|
||||
private void configure() {
|
||||
ThemeEditorDialog dialog = ThemeEditorDialog.getRunningInstance();
|
||||
if (dialog != null) {
|
||||
dialog.toFront();
|
||||
return;
|
||||
}
|
||||
|
||||
ThemeEditorDialog.editTheme(themeManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -37,7 +37,7 @@ import ghidra.util.HelpLocation;
|
||||
|
||||
class ImportGhidraToolsDialog extends DialogComponentProvider {
|
||||
|
||||
private ListPanel listPanel;
|
||||
private ListPanel<JCheckBox> listPanel;
|
||||
private JPanel mainPanel;
|
||||
private GCheckBox[] checkboxes;
|
||||
private String[] tools;
|
||||
@ -97,7 +97,7 @@ class ImportGhidraToolsDialog extends DialogComponentProvider {
|
||||
|
||||
SelectPanel myButtonPanel = new SelectPanel(e -> selectAll(), e -> deselectAll());
|
||||
|
||||
listPanel = new ListPanel();
|
||||
listPanel = new ListPanel<>();
|
||||
listPanel.setCellRenderer(new DataCellRenderer());
|
||||
listPanel.setMouseListener(new ListMouseListener());
|
||||
|
||||
|
@ -46,7 +46,7 @@ import ghidra.util.task.*;
|
||||
*/
|
||||
public class SaveDataDialog extends DialogComponentProvider {
|
||||
|
||||
private ListPanel listPanel;
|
||||
private ListPanel<JCheckBox> listPanel;
|
||||
private JPanel mainPanel;
|
||||
private GCheckBox[] checkboxes;
|
||||
private List<DomainFile> files;
|
||||
@ -156,7 +156,7 @@ public class SaveDataDialog extends DialogComponentProvider {
|
||||
|
||||
SelectPanel myButtonPanel = new SelectPanel(e -> selectAll(), e -> deselectAll());
|
||||
|
||||
listPanel = new ListPanel();
|
||||
listPanel = new ListPanel<>();
|
||||
listPanel.setCellRenderer(new DataCellRenderer());
|
||||
listPanel.setMouseListener(new ListMouseListener());
|
||||
|
||||
|
@ -36,11 +36,12 @@ class DomainFilesPanel extends JPanel {
|
||||
|
||||
private List<DomainFile> fileList;
|
||||
private GCheckBox[] checkboxes;
|
||||
private ListPanel listPanel;
|
||||
private ListPanel<JCheckBox> listPanel;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param fileList list of DomainFile objects
|
||||
* @param listTitle the title
|
||||
*/
|
||||
DomainFilesPanel(List<DomainFile> fileList, String listTitle) {
|
||||
super();
|
||||
@ -59,7 +60,7 @@ class DomainFilesPanel extends JPanel {
|
||||
//
|
||||
// List Panel
|
||||
//
|
||||
listPanel = new ListPanel();
|
||||
listPanel = new ListPanel<>();
|
||||
listPanel.setCellRenderer(new DataCellRenderer());
|
||||
listPanel.setMouseListener(new ListMouseListener());
|
||||
if (listTitle != null) {
|
||||
@ -121,7 +122,7 @@ class DomainFilesPanel extends JPanel {
|
||||
return;
|
||||
}
|
||||
|
||||
JList list = (JList) e.getSource();
|
||||
JList<?> list = (JList<?>) e.getSource();
|
||||
int index = list.locationToIndex(e.getPoint());
|
||||
if (index < 0) {
|
||||
return;
|
||||
|
@ -33,8 +33,9 @@ import generic.util.Path;
|
||||
import ghidra.app.plugin.core.console.ConsoleComponentProvider;
|
||||
import ghidra.app.plugin.core.osgi.BundleStatusComponentProvider;
|
||||
import ghidra.app.plugin.core.script.*;
|
||||
import ghidra.app.script.GhidraScriptUtil;
|
||||
import ghidra.app.script.*;
|
||||
import ghidra.app.services.ConsoleService;
|
||||
import ghidra.python.PythonScriptProvider;
|
||||
import ghidra.util.HelpLocation;
|
||||
|
||||
public class GhidraScriptMgrPluginScreenShots extends GhidraScreenShotGenerator {
|
||||
@ -230,10 +231,11 @@ public class GhidraScriptMgrPluginScreenShots extends GhidraScreenShotGenerator
|
||||
@Test
|
||||
public void testPick() {
|
||||
|
||||
List<String> items = new ArrayList<>();
|
||||
items.add("Java");
|
||||
items.add("Python");
|
||||
final PickProviderDialog pickDialog = new PickProviderDialog(items, "Java");
|
||||
List<GhidraScriptProvider> items = new ArrayList<>();
|
||||
JavaScriptProvider javaScriptProvider = new JavaScriptProvider();
|
||||
items.add(javaScriptProvider);
|
||||
items.add(new PythonScriptProvider());
|
||||
final PickProviderDialog pickDialog = new PickProviderDialog(items, javaScriptProvider);
|
||||
runSwing(() -> tool.showDialog(pickDialog), false);
|
||||
|
||||
PickProviderDialog dialog = waitForDialogComponent(PickProviderDialog.class);
|
||||
|
@ -22,6 +22,7 @@ import org.junit.Test;
|
||||
import docking.theme.gui.*;
|
||||
import generic.theme.*;
|
||||
import generic.theme.GThemeDefaults.Colors.Palette;
|
||||
import ghidra.app.plugin.gui.ThemeChooserDialog;
|
||||
import resources.ResourceManager;
|
||||
|
||||
public class ThemingScreenShots extends GhidraScreenShotGenerator {
|
||||
@ -34,11 +35,17 @@ public class ThemingScreenShots extends GhidraScreenShotGenerator {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThemeDialog() {
|
||||
showDialogWithoutBlocking(tool, new ThemeDialog(themeManager));
|
||||
public void testThemeEditorDialog() {
|
||||
showDialogWithoutBlocking(tool, new ThemeEditorDialog(themeManager));
|
||||
captureDialog(1000, 500);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThemeChooserDialog() {
|
||||
showDialogWithoutBlocking(tool, new ThemeChooserDialog(themeManager));
|
||||
captureDialog(250, 250);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testColorEditor() {
|
||||
ColorValueEditor editor = new ColorValueEditor(e -> {
|
||||
@ -63,7 +70,8 @@ public class ThemingScreenShots extends GhidraScreenShotGenerator {
|
||||
public void testIconEditor() {
|
||||
IconValueEditor editor = new IconValueEditor(e -> {
|
||||
/**/});
|
||||
IconValue value = new IconValue("icon.bomb", ResourceManager.getDefaultIcon());
|
||||
IconValue value =
|
||||
new IconValue("icon.reload", ResourceManager.loadIcon("images/reload.png"));
|
||||
themeManager.setIcon(value);
|
||||
editor.editValue(value);
|
||||
captureDialog();
|
||||
|