diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/AbstractGCellRenderer.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/AbstractGCellRenderer.java index e04d1b72a0..e901a502ab 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/AbstractGCellRenderer.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/AbstractGCellRenderer.java @@ -146,20 +146,29 @@ public abstract class AbstractGCellRenderer extends GDHtmlLabel { public void setFont(Font f) { super.setFont(f); + checkForInvalidSetFont(f); + } + + private void checkForInvalidSetFont(Font f) { // // Due to the nature of how setFont() is typically used (external client setup vs internal // rendering), we created setBaseFontId() to allow external clients to set the base font in // a way that is consistent with theming. Ignore any request to use one of our existing // fonts, as some clients may do that from the getTableCellRendererComponent() method. // - if (defaultFont != null && - !CollectionUtils.isOneOf(f, defaultFont, fixedWidthFont, boldFont)) { - - String caller = - ReflectionUtilities.getClassNameOlderThan(getClass().getName(), "generic.theme"); - Msg.debug(this, "Calling setFont() on the renderer is discouraged. " + - "To change the font, call setBaseFontId(). Called from " + caller); + if (defaultFont == null || + CollectionUtils.isOneOf(f, defaultFont, fixedWidthFont, boldFont)) { + return; } + + if (Gui.isUpdatingTheme()) { + return; // the UI will set fonts while the theme is updating + } + + String caller = ReflectionUtilities + .getClassNameOlderThan(AbstractGCellRenderer.class.getName(), "generic.theme"); + Msg.debug(this, "Calling setFont() on the renderer is discouraged. " + + "To change the font, call setBaseFontId(). Called from " + caller); } /** 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 ea5181ca50..585a6220bb 100644 --- a/Ghidra/Framework/Gui/src/main/java/generic/theme/ApplicationThemeManager.java +++ b/Ghidra/Framework/Gui/src/main/java/generic/theme/ApplicationThemeManager.java @@ -73,10 +73,12 @@ public class ApplicationThemeManager extends ThemeManager { @Override public void restoreThemeValues() { - applicationDefaults = loadApplicationDefaults(); - buildCurrentValues(); - lookAndFeelManager.resetAll(javaDefaults); - notifyThemeChanged(new AllValuesChangedThemeEvent(false)); + update(() -> { + applicationDefaults = loadApplicationDefaults(); + buildCurrentValues(); + lookAndFeelManager.resetAll(javaDefaults); + notifyThemeChanged(new AllValuesChangedThemeEvent(false)); + }); } @Override @@ -117,22 +119,30 @@ public class ApplicationThemeManager extends ThemeManager { @Override public void setTheme(GTheme theme) { - if (theme.hasSupportedLookAndFeel()) { + if (!theme.hasSupportedLookAndFeel()) { + Msg.error(this, + "Attempted to set theme with an unsupported Look and Feel: " + theme.getName()); + return; + } + + update(() -> { + activeTheme = theme; activeLafType = theme.getLookAndFeelType(); useDarkDefaults = theme.useDarkDefaults(); - cleanUiDefaults(); // clear out any values previous themes may have installed + cleanUiDefaults(); // clear out any values previous themes may have installed lookAndFeelManager = activeLafType.getLookAndFeelManager(this); try { lookAndFeelManager.installLookAndFeel(); + themePreferences.save(theme); notifyThemeChanged(new AllValuesChangedThemeEvent(true)); } catch (Exception e) { Msg.error(this, "Error setting Look and Feel: " + activeLafType.getName(), e); } - themePreferences.save(theme); - } + }); + currentValues.checkForUnresolvedReferences(); } @@ -146,15 +156,18 @@ public class ApplicationThemeManager extends ThemeManager { 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); - } + update(() -> { + + cleanUiDefaults(); // clear out any values previous themes may have installed + 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 @@ -207,15 +220,17 @@ public class ApplicationThemeManager extends ThemeManager { if (newValue.equals(currentValue)) { return; } - updateChangedValuesMap(currentValue, newValue); - currentValues.addFont(newValue); + update(() -> { + updateChangedValuesMap(currentValue, newValue); + currentValues.addFont(newValue); - // update all java LookAndFeel fonts affected by this changed - String id = newValue.getId(); - Set changedFontIds = findChangedJavaFontIds(id); - lookAndFeelManager.fontsChanged(changedFontIds); - notifyThemeChanged(new FontChangedThemeEvent(currentValues, newValue)); + // update all java LookAndFeel fonts affected by this changed + String id = newValue.getId(); + Set changedFontIds = findChangedJavaFontIds(id); + lookAndFeelManager.fontsChanged(changedFontIds); + notifyThemeChanged(new FontChangedThemeEvent(currentValues, newValue)); + }); } @Override @@ -224,10 +239,13 @@ public class ApplicationThemeManager extends ThemeManager { if (newValue.equals(currentValue)) { return; } - updateChangedValuesMap(currentValue, newValue); - currentValues.addColor(newValue); - notifyThemeChanged(new ColorChangedThemeEvent(currentValues, newValue)); - lookAndFeelManager.colorsChanged(); + + update(() -> { + updateChangedValuesMap(currentValue, newValue); + currentValues.addColor(newValue); + notifyThemeChanged(new ColorChangedThemeEvent(currentValues, newValue)); + lookAndFeelManager.colorsChanged(); + }); } @Override @@ -236,17 +254,18 @@ public class ApplicationThemeManager extends ThemeManager { if (newValue.equals(currentValue)) { return; } - updateChangedValuesMap(currentValue, newValue); - currentValues.addIcon(newValue); + update(() -> { + updateChangedValuesMap(currentValue, newValue); + currentValues.addIcon(newValue); - // now update the ui - // update all java LookAndFeel icons affected by this changed - String id = newValue.getId(); - Set changedIconIds = findChangedJavaIconIds(id); - Icon newIcon = newValue.get(currentValues); - lookAndFeelManager.iconsChanged(changedIconIds, newIcon); - notifyThemeChanged(new IconChangedThemeEvent(currentValues, newValue)); + // update all java LookAndFeel icons affected by this changed + String id = newValue.getId(); + Set changedIconIds = findChangedJavaIconIds(id); + Icon newIcon = newValue.get(currentValues); + lookAndFeelManager.iconsChanged(changedIconIds, newIcon); + notifyThemeChanged(new IconChangedThemeEvent(currentValues, newValue)); + }); } /** diff --git a/Ghidra/Framework/Gui/src/main/java/generic/theme/Gui.java b/Ghidra/Framework/Gui/src/main/java/generic/theme/Gui.java index c6bfb772bb..16e751dda7 100644 --- a/Ghidra/Framework/Gui/src/main/java/generic/theme/Gui.java +++ b/Ghidra/Framework/Gui/src/main/java/generic/theme/Gui.java @@ -198,6 +198,14 @@ public class Gui { return themeManager.isDarkTheme(); } + /** + * Returns true if the theme system is in the process of updating + * @return true if the theme system is in the process of updating + */ + public static boolean isUpdatingTheme() { + return themeManager.isUpdatingTheme(); + } + /** * Returns true if the given id is a system-defined id, such as those starting with * {@code laf.color} or {@code system.color}. diff --git a/Ghidra/Framework/Gui/src/main/java/generic/theme/ThemeManager.java b/Ghidra/Framework/Gui/src/main/java/generic/theme/ThemeManager.java index 990bea4df1..e0af423c4c 100644 --- a/Ghidra/Framework/Gui/src/main/java/generic/theme/ThemeManager.java +++ b/Ghidra/Framework/Gui/src/main/java/generic/theme/ThemeManager.java @@ -31,6 +31,7 @@ import ghidra.util.datastruct.WeakDataStructureFactory; import ghidra.util.datastruct.WeakSet; import resources.ResourceManager; import utilities.util.reflection.ReflectionUtilities; +import utility.function.Callback; /** * This class manages application themes and their values. The ThemeManager is an abstract @@ -81,6 +82,8 @@ public abstract class ThemeManager { private WeakSet themeListeners = WeakDataStructureFactory.createCopyOnReadWeakSet(); + private boolean isUpdating; + public static ThemeManager getInstance() { return INSTANCE; } @@ -616,6 +619,24 @@ public abstract class ThemeManager { } } + /** + * Returns true if the theme system is in the process of updating + * @return true if the theme system is in the process of updating + */ + public boolean isUpdatingTheme() { + return isUpdating; + } + + protected void update(Callback callback) { + isUpdating = true; + try { + callback.call(); + } + finally { + isUpdating = false; + } + } + protected void notifyThemeChanged(ThemeEvent event) { for (ThemeListener listener : themeListeners) { listener.themeChanged(event); diff --git a/Ghidra/Framework/Utility/src/main/java/utilities/util/reflection/ReflectionUtilities.java b/Ghidra/Framework/Utility/src/main/java/utilities/util/reflection/ReflectionUtilities.java index 0a360e8bb1..471cda171b 100644 --- a/Ghidra/Framework/Utility/src/main/java/utilities/util/reflection/ReflectionUtilities.java +++ b/Ghidra/Framework/Utility/src/main/java/utilities/util/reflection/ReflectionUtilities.java @@ -236,11 +236,9 @@ public class ReflectionUtilities { StackTraceElement[] trace = t.getStackTrace(); int lastIgnoreIndex = -1; for (int i = 0; i < trace.length; i++) { - StackTraceElement element = trace[i]; - String className = element.getClassName(); - int nameIndex = patterns.indexOf(className); - if (nameIndex != -1) { + String text = element.getClassName() + " " + element.getMethodName(); + if (containsAny(text, patterns)) { lastIgnoreIndex = i; } else { @@ -269,6 +267,15 @@ public class ReflectionUtilities { return t; } + private static boolean containsAny(String text, List patterns) { + for (String pattern : patterns) { + if (text.contains(pattern)) { + return true; + } + } + return false; + } + /** * Creates a throwable whose stack trace is based upon the current call stack, with any * information coming before, and including, the given classes removed.