diff --git a/Ghidra/Framework/Docking/src/main/java/docking/theme/gui/ThemeEditorDialog.java b/Ghidra/Framework/Docking/src/main/java/docking/theme/gui/ThemeEditorDialog.java index 9dcf7da99c..481b601df8 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/theme/gui/ThemeEditorDialog.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/theme/gui/ThemeEditorDialog.java @@ -39,7 +39,7 @@ public class ThemeEditorDialog extends DialogComponentProvider { private JButton saveButton; private GhidraComboBox combo; - private ItemListener comboListener = this::themeComboChanged; + private ItemListener comboListener = this::lafTypeComboChanged; private ThemeListener listener = new DialogThemeListener(); private JTabbedPane tabbedPane; @@ -176,9 +176,16 @@ public class ThemeEditorDialog extends DialogComponentProvider { private void resetSelectedLookAndFeel() { Swing.runLater(() -> { + + LafType lafType = themeManager.getLookAndFeelType(); + Object currentItem = combo.getSelectedItem(); + if (lafType == currentItem) { + return; + } + try { combo.removeItemListener(comboListener); - combo.setSelectedItem(themeManager.getActiveTheme().getLookAndFeelType()); + combo.setSelectedItem(lafType); } finally { combo.addItemListener(comboListener); @@ -186,14 +193,21 @@ public class ThemeEditorDialog extends DialogComponentProvider { }); } - private void themeComboChanged(ItemEvent e) { + private void lafTypeComboChanged(ItemEvent e) { if (e.getStateChange() != ItemEvent.SELECTED) { return; } LafType lafType = (LafType) e.getItem(); + LafType currentLafType = themeManager.getLookAndFeelType(); + if (currentLafType == lafType) { + return; + } + if (!themeManager.hasThemeValueChanges()) { + // This allows the user to toggle the them lafType repeatedly without having to save, as + // long as they have not changed any other theme values. setLookAndFeel(lafType); return; } @@ -257,7 +271,6 @@ public class ThemeEditorDialog extends DialogComponentProvider { private Component buildThemeCombo() { JPanel panel = new JPanel(); List lafs = getSupportedLookAndFeels(); - combo = new GhidraComboBox<>(lafs); combo.setSelectedItem(themeManager.getActiveTheme().getLookAndFeelType()); combo.addItemListener(comboListener); @@ -270,7 +283,8 @@ public class ThemeEditorDialog extends DialogComponentProvider { private List getSupportedLookAndFeels() { LafType[] lafTypes = LafType.values(); - Comparator comparator = (a, b) -> a.getName().compareTo(b.getName()); + Comparator comparator = + (a, b) -> a.getDisplayString().compareTo(b.getDisplayString()); return Arrays.stream(lafTypes).filter(laf -> laf.isSupported()).sorted(comparator).toList(); } @@ -334,6 +348,10 @@ public class ThemeEditorDialog extends DialogComponentProvider { return contextProvider.getActionContext(event); } +//================================================================================================= +// Inner Classes +//================================================================================================= + private class DialogThemeListener implements ThemeListener { @Override public void themeChanged(ThemeEvent event) { @@ -355,7 +373,7 @@ public class ThemeEditorDialog extends DialogComponentProvider { } updateButtons(); + resetSelectedLookAndFeel(); } } - } diff --git a/Ghidra/Framework/Gui/data/gui.theme.properties b/Ghidra/Framework/Gui/data/gui.theme.properties index 498bf2410d..149af8a9bb 100644 --- a/Ghidra/Framework/Gui/data/gui.theme.properties +++ b/Ghidra/Framework/Gui/data/gui.theme.properties @@ -12,7 +12,7 @@ color.fg.messages.hint = color.palette.hint color.fg.messages.alert = color.palette.orange color.fg.messages.error = color.fg.error color.fg.messages.normal = color.palette.blue -color.fg.messages.warning = color.palette.orange +color.fg.messages.warning = color.palette.saddlebrown color.cursor.focused = color.palette.red color.cursor.unfocused = color.palette.pink diff --git a/Ghidra/Framework/Gui/src/main/java/generic/theme/ApplicationThemeManager.java b/Ghidra/Framework/Gui/src/main/java/generic/theme/ApplicationThemeManager.java index 585a6220bb..f5813b6ca4 100644 --- a/Ghidra/Framework/Gui/src/main/java/generic/theme/ApplicationThemeManager.java +++ b/Ghidra/Framework/Gui/src/main/java/generic/theme/ApplicationThemeManager.java @@ -148,6 +148,11 @@ public class ApplicationThemeManager extends ThemeManager { @Override public void setLookAndFeel(LafType lafType, boolean useDarkDefaults) { + + if (activeLafType == lafType) { + return; + } + if (!lafType.isSupported()) { Msg.error(this, "Attempted to set unsupported Look and Feel: " + lafType); return; @@ -173,7 +178,7 @@ public class ApplicationThemeManager extends ThemeManager { @Override public void addTheme(GTheme newTheme) { loadThemes(); - allThemes.remove(newTheme); + removeTheme(newTheme); allThemes.add(newTheme); } @@ -183,8 +188,33 @@ public class ApplicationThemeManager extends ThemeManager { if (file != null) { file.delete(); } - if (allThemes != null) { - allThemes.remove(theme); + + removeTheme(theme); + } + + /** + * Removes the given theme from the set of themes known by this class. + *

+ * Note: this method assumes that there can only exist one theme by a given name in the system. + * The equals() method of the GTheme is based on more than just the name, so we cannot use that + * to remove the theme from this class. + * + * @param t the theme to remove + */ + private void removeTheme(GTheme t) { + if (allThemes == null) { + return; + } + + String nameToDelete = t.getName(); + Iterator it = allThemes.iterator(); + while (it.hasNext()) { + GTheme theme = it.next(); + String name = theme.getName(); + if (name.equals(nameToDelete)) { + it.remove(); + return; + } } } diff --git a/Ghidra/Framework/Gui/src/main/java/generic/theme/GTheme.java b/Ghidra/Framework/Gui/src/main/java/generic/theme/GTheme.java index 228e0c9117..4e8208a65c 100644 --- a/Ghidra/Framework/Gui/src/main/java/generic/theme/GTheme.java +++ b/Ghidra/Framework/Gui/src/main/java/generic/theme/GTheme.java @@ -229,7 +229,7 @@ public class GTheme extends GThemeValueMap { * a theme file and optionally a set of icon files. * @param file the file to read. * @return the theme that was read from the file - * @throws IOException if an error occcured trying to read a theme from the file. + * @throws IOException if an error occurred trying to read a theme from the file. */ public static GTheme loadTheme(File file) throws IOException { ThemeReader reader = new ThemeReader(file); diff --git a/Ghidra/Framework/Gui/src/main/java/generic/theme/LafType.java b/Ghidra/Framework/Gui/src/main/java/generic/theme/LafType.java index cba9cd3402..e261e524ff 100644 --- a/Ghidra/Framework/Gui/src/main/java/generic/theme/LafType.java +++ b/Ghidra/Framework/Gui/src/main/java/generic/theme/LafType.java @@ -50,6 +50,19 @@ public enum LafType { this.usesDarkDefaults = usesDarkDefaults; } + /** + * Gets the preferred display string for this type. + * @return the preferred display string. + */ + public String getDisplayString() { + if (this == MOTIF) { + // The name is "CDE/Motif"; Update to be consistent with other dialogs, like the theme + // switcher dialog + return "Motif"; + } + return name; + } + /** * Returns the name of this LafType. * @return the name of this LafType. @@ -154,6 +167,6 @@ public enum LafType { @Override public String toString() { - return getName(); + return getDisplayString(); } }