mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-21 18:01:57 +08:00
GP-1981Checkpoint - more Changes
This commit is contained in:
+3
-2
@@ -22,6 +22,7 @@ import java.util.*;
|
||||
import javax.swing.*;
|
||||
import javax.swing.tree.DefaultTreeCellRenderer;
|
||||
|
||||
import docking.theme.GColor;
|
||||
import docking.widgets.GComponent;
|
||||
import ghidra.program.model.listing.Group;
|
||||
import resources.ResourceManager;
|
||||
@@ -67,8 +68,8 @@ class DnDTreeCellRenderer extends DefaultTreeCellRenderer {
|
||||
*/
|
||||
DnDTreeCellRenderer() {
|
||||
super();
|
||||
defaultNonSelectionColor = getBackgroundNonSelectionColor();
|
||||
defaultSelectionColor = getBackgroundSelectionColor();
|
||||
defaultNonSelectionColor = new GColor("Tree.textBackground");
|
||||
defaultSelectionColor = new GColor("Tree.selectionBackground");
|
||||
rowForFeedback = -1;
|
||||
|
||||
// disable HTML rendering
|
||||
|
||||
@@ -15,32 +15,23 @@
|
||||
*/
|
||||
package ghidra.app.plugin.gui;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import docking.action.builder.ActionBuilder;
|
||||
import docking.options.editor.StringWithChoicesEditor;
|
||||
import docking.theme.GTheme;
|
||||
import docking.theme.GThemeDialog;
|
||||
import docking.theme.Gui;
|
||||
import docking.theme.*;
|
||||
import docking.theme.gui.GThemeDialog;
|
||||
import docking.tool.ToolConstants;
|
||||
import ghidra.app.CorePluginPackage;
|
||||
import ghidra.app.plugin.PluginCategoryNames;
|
||||
import ghidra.docking.util.LookAndFeelUtils;
|
||||
import ghidra.framework.main.FrontEndOnly;
|
||||
import ghidra.framework.main.FrontEndTool;
|
||||
import ghidra.framework.options.OptionType;
|
||||
import ghidra.framework.options.OptionsChangeListener;
|
||||
import ghidra.framework.options.ToolOptions;
|
||||
import ghidra.framework.plugintool.Plugin;
|
||||
import ghidra.framework.plugintool.PluginInfo;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.framework.options.*;
|
||||
import ghidra.framework.plugintool.*;
|
||||
import ghidra.framework.plugintool.util.PluginStatus;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.SystemUtilities;
|
||||
import ghidra.util.*;
|
||||
|
||||
//@formatter:off
|
||||
@PluginInfo(
|
||||
@@ -70,13 +61,11 @@ public class ThemeManagerPlugin extends Plugin implements FrontEndOnly, OptionsC
|
||||
|
||||
@Override
|
||||
protected void init() {
|
||||
new ActionBuilder("Dump UI Properties", getName())
|
||||
.menuPath("Edit", "Dump UI Properies")
|
||||
new ActionBuilder("Dump UI Properties", getName()).menuPath("Edit", "Dump UI Properies")
|
||||
.onAction(e -> LookAndFeelUtils.dumpUIProperties())
|
||||
.buildAndInstall(tool);
|
||||
|
||||
new ActionBuilder("Show Properties", getName())
|
||||
.menuPath("Edit", "Theme Properties")
|
||||
new ActionBuilder("Show Properties", getName()).menuPath("Edit", "Theme Properties")
|
||||
.onAction(e -> showThemeProperties())
|
||||
.buildAndInstall(tool);
|
||||
|
||||
@@ -92,7 +81,7 @@ public class ThemeManagerPlugin extends Plugin implements FrontEndOnly, OptionsC
|
||||
ToolOptions opt = tool.getOptions(OPTIONS_TITLE);
|
||||
|
||||
GTheme activeTheme = Gui.getActiveTheme();
|
||||
List<String> themeNames = getAllThemeNames();
|
||||
List<String> themeNames = Gui.getAllThemeNames();
|
||||
|
||||
opt.registerOption(THEME_OPTIONS_NAME, OptionType.STRING_TYPE, activeTheme.getName(),
|
||||
new HelpLocation(ToolConstants.TOOL_HELP_TOPIC, "Look_And_Feel"),
|
||||
@@ -103,14 +92,6 @@ public class ThemeManagerPlugin extends Plugin implements FrontEndOnly, OptionsC
|
||||
opt.addOptionsChangeListener(this);
|
||||
}
|
||||
|
||||
private List<String> getAllThemeNames() {
|
||||
Set<GTheme> allThemes = Gui.getAllThemes();
|
||||
List<String> themeNames =
|
||||
allThemes.stream().map(t -> t.getName()).collect(Collectors.toList());
|
||||
Collections.sort(themeNames);
|
||||
return themeNames;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void optionsChanged(ToolOptions options, String optionName, Object oldValue,
|
||||
Object newValue) {
|
||||
|
||||
@@ -27,7 +27,7 @@ public class ColorValue extends ThemeValue<Color> {
|
||||
public static final Color LAST_RESORT_DEFAULT = Color.GRAY;
|
||||
|
||||
public ColorValue(String id, Color value) {
|
||||
super(id, null, value);
|
||||
super(id, getRefId(value), getRawColor(value));
|
||||
}
|
||||
|
||||
public ColorValue(String id, String refId) {
|
||||
@@ -96,4 +96,19 @@ public class ColorValue extends ThemeValue<Color> {
|
||||
float[] hsb = Color.RGBtoHSB(v.getRed(), v.getGreen(), v.getBlue(), null);
|
||||
return 100 * (int) (10 * hsb[0]) + 10 * (int) (10 * hsb[1]) + (int) (10 * hsb[2]);
|
||||
}
|
||||
|
||||
private static Color getRawColor(Color value) {
|
||||
if (value instanceof GColor) {
|
||||
return null;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private static String getRefId(Color value) {
|
||||
if (value instanceof GColor) {
|
||||
return ((GColor) value).getId();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -20,35 +20,41 @@ import java.awt.color.ColorSpace;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.awt.image.ColorModel;
|
||||
import java.util.Objects;
|
||||
|
||||
import ghidra.util.datastruct.WeakDataStructureFactory;
|
||||
import ghidra.util.datastruct.WeakSet;
|
||||
import ghidra.util.datastruct.WeakStore;
|
||||
|
||||
public class GColor extends Color implements Refreshable {
|
||||
private static WeakSet<GColor> inUseColors = WeakDataStructureFactory.createCopyOnReadWeakSet();
|
||||
private static WeakStore<GColor> inUseColors = new WeakStore<>();
|
||||
private String id;
|
||||
private Color delegate;
|
||||
|
||||
public static void refreshAll() {
|
||||
for (GColor gcolor : inUseColors) {
|
||||
for (GColor gcolor : inUseColors.getValues()) {
|
||||
gcolor.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
public GColor(String id) {
|
||||
this(id, true);
|
||||
}
|
||||
|
||||
public GColor(String id, boolean validate) {
|
||||
super(0x808080);
|
||||
this.id = id;
|
||||
delegate = Gui.getRawColor(id);
|
||||
if (delegate == null) {
|
||||
delegate = Color.gray;
|
||||
}
|
||||
delegate = Gui.getRawColor(id, validate);
|
||||
inUseColors.add(this);
|
||||
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public boolean isEquivalent(Color color) {
|
||||
return delegate.getRGB() == color.getRGB();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRed() {
|
||||
return delegate.getRed();
|
||||
@@ -86,12 +92,7 @@ public class GColor extends Color implements Refreshable {
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return delegate.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return delegate.equals(obj);
|
||||
return id.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -99,6 +100,21 @@ public class GColor extends Color implements Refreshable {
|
||||
return getClass().getName() + " [id = " + id + ", " + delegate.toString() + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!super.equals(obj)) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
GColor other = (GColor) obj;
|
||||
return Objects.equals(id, other.id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float[] getRGBComponents(float[] compArray) {
|
||||
return delegate.getRGBComponents(compArray);
|
||||
@@ -147,9 +163,12 @@ public class GColor extends Color implements Refreshable {
|
||||
|
||||
@Override
|
||||
public void refresh() {
|
||||
Color color = Gui.getRawColor(id);
|
||||
Color color = Gui.getRawColor(id, false);
|
||||
if (color != null) {
|
||||
delegate = color;
|
||||
}
|
||||
else {
|
||||
System.out.println("Hey");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package docking.theme;
|
||||
|
||||
import javax.swing.plaf.UIResource;
|
||||
|
||||
public class GColorUIResource extends GColor implements UIResource {
|
||||
|
||||
public GColorUIResource(String id) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -21,8 +21,7 @@ import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.text.AttributedCharacterIterator.Attribute;
|
||||
import java.text.CharacterIterator;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
public class GFont extends Font implements Refreshable {
|
||||
|
||||
@@ -38,6 +37,10 @@ public class GFont extends Font implements Refreshable {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isEquivalent(Font font) {
|
||||
return delegate.equals(font);
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
@@ -128,12 +131,22 @@ public class GFont extends Font implements Refreshable {
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return delegate.hashCode();
|
||||
return id.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return delegate.equals(obj);
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!super.equals(obj)) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
GFont other = (GFont) obj;
|
||||
return Objects.equals(id, other.id);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -62,4 +62,24 @@ public class GIcon implements Icon, Refreshable {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return id.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
GIcon other = (GIcon) obj;
|
||||
return id.equals(other.id);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -200,7 +200,7 @@ public class GTheme extends GThemeValueMap {
|
||||
List<FontValue> fonts = values.getFonts();
|
||||
Collections.sort(fonts);
|
||||
|
||||
List<IconValue> icons = values.getIconPaths();
|
||||
List<IconValue> icons = values.getIcons();
|
||||
Collections.sort(icons);
|
||||
|
||||
writer.write(THEME_NAME_KEY + " = " + name);
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package docking.theme;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Component;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import docking.DialogComponentProvider;
|
||||
import docking.widgets.filechooser.GhidraFileChooser;
|
||||
import docking.widgets.filechooser.GhidraFileChooserMode;
|
||||
import docking.widgets.table.GFilterTable;
|
||||
import docking.widgets.table.GTable;
|
||||
import ghidra.util.filechooser.GhidraFileFilter;
|
||||
|
||||
public class GThemeDialog extends DialogComponentProvider {
|
||||
|
||||
public GThemeDialog() {
|
||||
super("Theme Dialog", false);
|
||||
addWorkPanel(createMainPanel());
|
||||
addOKButton();
|
||||
addCancelButton();
|
||||
setOkButtonText("Save");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void okCallback() {
|
||||
GhidraFileChooser chooser = new GhidraFileChooser(getComponent());
|
||||
chooser.setTitle("Choose Theme File");
|
||||
chooser.setApproveButtonText("Select Output File");
|
||||
chooser.setApproveButtonToolTipText("Select File");
|
||||
chooser.setFileSelectionMode(GhidraFileChooserMode.FILES_ONLY);
|
||||
chooser.setSelectedFileFilter(GhidraFileFilter.ALL);
|
||||
File file = chooser.getSelectedFile();
|
||||
try {
|
||||
Gui.getActiveTheme().saveToFile(file, Gui.getAllDefaultValues());
|
||||
}
|
||||
catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private JComponent createMainPanel() {
|
||||
JPanel panel = new JPanel();
|
||||
|
||||
panel.setLayout(new BorderLayout());
|
||||
|
||||
panel.add(buildTabedTables());
|
||||
return panel;
|
||||
}
|
||||
|
||||
private Component buildTabedTables() {
|
||||
JTabbedPane tabbedPane = new JTabbedPane();
|
||||
tabbedPane.add("Colors", buildColorTable());
|
||||
return tabbedPane;
|
||||
}
|
||||
|
||||
private JComponent buildColorTable() {
|
||||
ThemeColorTableModel colorTableModel = new ThemeColorTableModel(Gui.getActiveTheme());
|
||||
|
||||
GTable colorTable = new GTable(colorTableModel);
|
||||
colorTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
GFilterTable<ColorValue> filterTable = new GFilterTable<>(colorTableModel);
|
||||
filterTable.getTable().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
filterTable.getTable()
|
||||
.setDefaultEditor(ColorValue.class,
|
||||
new ThemeColorEditor(Gui.getAllValues(), colorTableModel));
|
||||
return filterTable;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -74,7 +74,7 @@ public class GThemeValueMap {
|
||||
return new ArrayList<>(fontMap.values());
|
||||
}
|
||||
|
||||
public List<IconValue> getIconPaths() {
|
||||
public List<IconValue> getIcons() {
|
||||
return new ArrayList<>(iconMap.values());
|
||||
}
|
||||
|
||||
@@ -93,4 +93,10 @@ public class GThemeValueMap {
|
||||
public Object size() {
|
||||
return colorMap.size() + fontMap.size() + iconMap.size();
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
colorMap.clear();
|
||||
fontMap.clear();
|
||||
iconMap.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,8 +19,10 @@ import java.awt.*;
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.plaf.UIResource;
|
||||
|
||||
import docking.framework.ApplicationInformationDisplayFactory;
|
||||
import ghidra.docking.util.LookAndFeelUtils;
|
||||
@@ -42,13 +44,18 @@ public class Gui {
|
||||
private static Set<GTheme> allThemes;
|
||||
|
||||
private static GThemeValueMap ghidraCoreDefaults = new GThemeValueMap();
|
||||
private static GThemeValueMap javaDefaults;
|
||||
private static GThemeValueMap originalJavaDefaults;
|
||||
// private static GThemeValueMap convertedJavaDefaults;
|
||||
private static GThemeValueMap currentValues = new GThemeValueMap();
|
||||
|
||||
private static GThemeValueMap darkDefaults = new GThemeValueMap();
|
||||
|
||||
private static ThemePropertiesLoader themePropertiesLoader = new ThemePropertiesLoader();
|
||||
|
||||
private static Map<String, GColorUIResource> gColorMap = new HashMap<>();
|
||||
|
||||
private static JPanel jPanel;
|
||||
|
||||
static void setPropertiesLoader(ThemePropertiesLoader loader) {
|
||||
themePropertiesLoader = loader;
|
||||
}
|
||||
@@ -58,7 +65,6 @@ public class Gui {
|
||||
}
|
||||
|
||||
public static void initialize() {
|
||||
themePropertiesLoader.initialize();
|
||||
loadThemeDefaults();
|
||||
setTheme(getThemeFromPreferences());
|
||||
LookAndFeelUtils.installGlobalOverrides();
|
||||
@@ -66,28 +72,60 @@ public class Gui {
|
||||
}
|
||||
|
||||
private static void loadThemeDefaults() {
|
||||
themePropertiesLoader.load();
|
||||
ghidraCoreDefaults = themePropertiesLoader.getDefaults();
|
||||
darkDefaults = themePropertiesLoader.getDarkDefaults();
|
||||
}
|
||||
|
||||
public static void reloadThemeDefaults() {
|
||||
loadThemeDefaults();
|
||||
currentValues = buildCurrentValues(activeTheme);
|
||||
refresh();
|
||||
}
|
||||
|
||||
public static void setTheme(GTheme theme) {
|
||||
activeTheme = theme;
|
||||
LookAndFeelUtils.setLookAndFeel(theme.getLookAndFeelName());
|
||||
javaDefaults = mineJavaDefaults();
|
||||
currentValues = buildCurrentValues(theme);
|
||||
installBackIntoJava();
|
||||
refresh();
|
||||
}
|
||||
|
||||
private static void installBackIntoJava() {
|
||||
UIDefaults defaults = UIManager.getDefaults();
|
||||
for (ColorValue color : javaDefaults.getColors()) {
|
||||
String id = color.getId();
|
||||
defaults.put(id, new GColor(id));
|
||||
private static void refresh() {
|
||||
GColor.refreshAll();
|
||||
for (Window window : Window.getWindows()) {
|
||||
SwingUtilities.updateComponentTreeUI(window);
|
||||
}
|
||||
}
|
||||
|
||||
// private static GThemeValueMap convertJavaDefaults(GThemeValueMap input) {
|
||||
// GThemeValueMap converted = new GThemeValueMap();
|
||||
// for (ColorValue colorValue : input.getColors()) {
|
||||
// converted.addColor(fromUiResource(colorValue));
|
||||
// }
|
||||
// for (FontValue fontValue : input.getFonts()) {
|
||||
// converted.addFont(fromUiResource(fontValue));
|
||||
// }
|
||||
// // java icons are not currently supported
|
||||
// return converted;
|
||||
// }
|
||||
|
||||
private static FontValue fromUiResource(FontValue fontValue) {
|
||||
Font font = fontValue.getRawValue();
|
||||
if (font instanceof UIResource) {
|
||||
return new FontValue(fontValue.getId(), font.deriveFont(font.getStyle()));
|
||||
}
|
||||
return fontValue;
|
||||
}
|
||||
|
||||
private static ColorValue fromUiResource(ColorValue colorValue) {
|
||||
Color color = colorValue.getRawValue();
|
||||
if (color instanceof UIResource) {
|
||||
return new ColorValue(colorValue.getId(), new Color(color.getRGB(), true));
|
||||
}
|
||||
return colorValue;
|
||||
}
|
||||
|
||||
public static boolean isJavaDefinedColor(String id) {
|
||||
return javaDefaults.containsColor(id);
|
||||
return originalJavaDefaults.containsColor(id);
|
||||
}
|
||||
|
||||
public static GThemeValueMap getAllValues() {
|
||||
@@ -96,7 +134,7 @@ public class Gui {
|
||||
|
||||
public static GThemeValueMap getAllDefaultValues() {
|
||||
GThemeValueMap currentDefaults = new GThemeValueMap();
|
||||
currentDefaults.load(javaDefaults);
|
||||
currentDefaults.load(originalJavaDefaults);
|
||||
currentDefaults.load(ghidraCoreDefaults);
|
||||
if (activeTheme.isDark()) {
|
||||
currentDefaults.load(darkDefaults);
|
||||
@@ -111,6 +149,19 @@ public class Gui {
|
||||
return Collections.unmodifiableSet(allThemes);
|
||||
}
|
||||
|
||||
public static GTheme getTheme(String themeName) {
|
||||
Optional<GTheme> first =
|
||||
getAllThemes().stream().filter(t -> t.getName().equals(themeName)).findFirst();
|
||||
return first.get();
|
||||
}
|
||||
|
||||
public static List<String> getAllThemeNames() {
|
||||
List<String> themeNames =
|
||||
getAllThemes().stream().map(t -> t.getName()).collect(Collectors.toList());
|
||||
Collections.sort(themeNames);
|
||||
return themeNames;
|
||||
}
|
||||
|
||||
public static Color darker(Color color) {
|
||||
if (activeTheme.isDark()) {
|
||||
return color.brighter();
|
||||
@@ -157,13 +208,19 @@ public class Gui {
|
||||
}
|
||||
}
|
||||
|
||||
static Color getRawColor(String id) {
|
||||
ColorValue color = currentValues.getColor(id);
|
||||
if (color == null) {
|
||||
Throwable t = getFilteredTrace();
|
||||
public static Color getRawColor(String id) {
|
||||
return getRawColor(id, true);
|
||||
}
|
||||
|
||||
Msg.error(Gui.class, "No color value registered for: " + id, t);
|
||||
return null;
|
||||
static Color getRawColor(String id, boolean validate) {
|
||||
ColorValue color = currentValues.getColor(id);
|
||||
|
||||
if (color == null) {
|
||||
if (validate) {
|
||||
// Throwable t = getFilteredTrace();
|
||||
Msg.error(Gui.class, "No color value registered for: " + id);
|
||||
}
|
||||
return Color.CYAN;
|
||||
}
|
||||
return color.get(currentValues);
|
||||
}
|
||||
@@ -203,7 +260,7 @@ public class Gui {
|
||||
private static GThemeValueMap buildCurrentValues(GTheme theme) {
|
||||
GThemeValueMap map = new GThemeValueMap();
|
||||
|
||||
map.load(javaDefaults);
|
||||
map.load(originalJavaDefaults);
|
||||
map.load(ghidraCoreDefaults);
|
||||
if (theme.isDark()) {
|
||||
map.load(darkDefaults);
|
||||
@@ -212,18 +269,13 @@ public class Gui {
|
||||
return map;
|
||||
}
|
||||
|
||||
private static GThemeValueMap mineJavaDefaults() {
|
||||
GThemeValueMap values = new GThemeValueMap();
|
||||
// for now, just doing color properties.
|
||||
List<String> ids = LookAndFeelUtils.getLookAndFeelIdsForType(Color.class);
|
||||
for (String id : ids) {
|
||||
// Create a new color to ensure we are not storing a UIResource; otherwise java
|
||||
// java ignore the color because the UI widgets take liberties when UIResources
|
||||
// are being used.
|
||||
Color lafColor = new Color(UIManager.getColor(id).getRGB(), true);
|
||||
values.addColor(new ColorValue(id, lafColor));
|
||||
private static Color getUIColor(String id) {
|
||||
// Not sure, but for now, make sure colors are not UIResource
|
||||
Color color = UIManager.getColor(id);
|
||||
if (color instanceof UIResource) {
|
||||
return new Color(color.getRGB(), true);
|
||||
}
|
||||
return values;
|
||||
return color;
|
||||
}
|
||||
|
||||
private static Set<GTheme> findThemes() {
|
||||
@@ -297,7 +349,7 @@ public class Gui {
|
||||
|
||||
public static GThemeValueMap getCoreDefaults() {
|
||||
GThemeValueMap map = new GThemeValueMap(ghidraCoreDefaults);
|
||||
map.load(javaDefaults);
|
||||
map.load(originalJavaDefaults);
|
||||
return map;
|
||||
}
|
||||
|
||||
@@ -308,9 +360,34 @@ public class Gui {
|
||||
}
|
||||
|
||||
public static void setColor(String id, Color color) {
|
||||
currentValues.addColor(new ColorValue(id, color));
|
||||
GColor.refreshAll();
|
||||
setColor(new ColorValue(id, color));
|
||||
}
|
||||
|
||||
public static void setColor(ColorValue colorValue) {
|
||||
currentValues.addColor(colorValue);
|
||||
System.out.println("Change color: " + colorValue);
|
||||
GColor.refreshAll();
|
||||
for (Window window : Window.getWindows()) {
|
||||
window.repaint();
|
||||
}
|
||||
}
|
||||
|
||||
public static GColorUIResource getGColorUiResource(String id) {
|
||||
GColorUIResource gColor = gColorMap.get(id);
|
||||
if (gColor == null) {
|
||||
gColor = new GColorUIResource(id);
|
||||
gColorMap.put(id, gColor);
|
||||
}
|
||||
return gColor;
|
||||
}
|
||||
|
||||
public static void setJavaDefaults(GThemeValueMap javaDefaults) {
|
||||
originalJavaDefaults = javaDefaults;
|
||||
currentValues = buildCurrentValues(activeTheme);
|
||||
}
|
||||
|
||||
public static GThemeValueMap getJavaDefaults() {
|
||||
return originalJavaDefaults;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,165 +0,0 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package docking.theme;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.EventObject;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.BevelBorder;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
import javax.swing.event.ChangeListener;
|
||||
import javax.swing.table.TableCellEditor;
|
||||
|
||||
import docking.DialogComponentProvider;
|
||||
import docking.DockingWindowManager;
|
||||
import docking.options.editor.GhidraColorChooser;
|
||||
import docking.widgets.label.GDLabel;
|
||||
|
||||
public class ThemeColorEditor extends AbstractCellEditor implements TableCellEditor {
|
||||
private GhidraColorChooser colorChooser;
|
||||
private Color lastUserSelectedColor;
|
||||
private Color color;
|
||||
|
||||
private ColorDialogProvider dialog;
|
||||
private JTable table;
|
||||
private ColorValue colorValue;
|
||||
|
||||
private GThemeValueMap values;
|
||||
private ThemeColorTableModel model;
|
||||
|
||||
public ThemeColorEditor(GThemeValueMap values, ThemeColorTableModel model) {
|
||||
this.values = values;
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getTableCellEditorComponent(JTable theTable, Object value, boolean isSelected,
|
||||
int row, int column) {
|
||||
|
||||
this.table = theTable;
|
||||
colorValue = (ColorValue) value;
|
||||
|
||||
JLabel label = new GDLabel();
|
||||
label.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED));
|
||||
label.setText(colorValue.getId());
|
||||
|
||||
dialog = new ColorDialogProvider();
|
||||
dialog.setRememberSize(false);
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
DockingWindowManager.showDialog(dialog);
|
||||
stopCellEditing();
|
||||
}
|
||||
});
|
||||
|
||||
return label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelCellEditing() {
|
||||
dialog.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getCellEditorValue() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean stopCellEditing() {
|
||||
ListSelectionModel columnSelectionModel = table.getColumnModel().getSelectionModel();
|
||||
columnSelectionModel.setValueIsAdjusting(true);
|
||||
int columnAnchor = columnSelectionModel.getAnchorSelectionIndex();
|
||||
int columnLead = columnSelectionModel.getLeadSelectionIndex();
|
||||
|
||||
if (color != null) {
|
||||
Gui.setColor(colorValue.getId(), color);
|
||||
model.refresh();
|
||||
}
|
||||
dialog.close();
|
||||
fireEditingStopped();
|
||||
|
||||
columnSelectionModel.setAnchorSelectionIndex(columnAnchor);
|
||||
columnSelectionModel.setLeadSelectionIndex(columnLead);
|
||||
columnSelectionModel.setValueIsAdjusting(false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// only double-click edits
|
||||
@Override
|
||||
public boolean isCellEditable(EventObject anEvent) {
|
||||
if (anEvent instanceof MouseEvent) {
|
||||
return ((MouseEvent) anEvent).getClickCount() >= 2;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Inner Classes
|
||||
//==================================================================================================
|
||||
|
||||
class ColorDialogProvider extends DialogComponentProvider {
|
||||
ColorDialogProvider() {
|
||||
super("Color Editor", true);
|
||||
|
||||
addWorkPanel(new ColorEditorPanel());
|
||||
addOKButton();
|
||||
addCancelButton();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void okCallback() {
|
||||
color = lastUserSelectedColor;
|
||||
close();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void cancelCallback() {
|
||||
color = null;
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
class ColorEditorPanel extends JPanel {
|
||||
|
||||
ColorEditorPanel() {
|
||||
|
||||
setLayout(new BorderLayout());
|
||||
|
||||
if (colorChooser == null) {
|
||||
colorChooser = new GhidraColorChooser();
|
||||
}
|
||||
|
||||
add(colorChooser, BorderLayout.CENTER);
|
||||
colorChooser.getSelectionModel().addChangeListener(new ChangeListener() {
|
||||
|
||||
@Override
|
||||
public void stateChanged(ChangeEvent e) {
|
||||
lastUserSelectedColor = colorChooser.getColor();
|
||||
// This could be a ColorUIResource, but Options only support storing Color.
|
||||
lastUserSelectedColor =
|
||||
new Color(lastUserSelectedColor.getRed(), lastUserSelectedColor.getGreen(),
|
||||
lastUserSelectedColor.getBlue(), lastUserSelectedColor.getAlpha());
|
||||
}
|
||||
});
|
||||
colorChooser.setColor(colorValue.get(values));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,9 +29,11 @@ public class ThemePropertiesLoader {
|
||||
ThemePropertiesLoader() {
|
||||
}
|
||||
|
||||
public void initialize() {
|
||||
public void load() {
|
||||
List<ResourceFile> themeDefaultFiles =
|
||||
Application.findFilesByExtensionInApplication(".theme.properties");
|
||||
defaults.clear();
|
||||
darkDefaults.clear();
|
||||
|
||||
for (ResourceFile resourceFile : themeDefaultFiles) {
|
||||
Msg.debug(this, "found theme file: " + resourceFile.getAbsolutePath());
|
||||
|
||||
@@ -24,12 +24,15 @@ public abstract class ThemeValue<T> implements Comparable<ThemeValue<T>> {
|
||||
private final String id;
|
||||
private final T value;
|
||||
private final String refId;
|
||||
private T cachedValue;
|
||||
// private T cachedValue;
|
||||
|
||||
protected ThemeValue(String id, String refId, T value) {
|
||||
this.id = fromExternalId(id);
|
||||
this.refId = (refId == null) ? null : fromExternalId(refId);
|
||||
this.value = value;
|
||||
if (value instanceof GColor) {
|
||||
System.out.println("Whoa");
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract String getIdPrefix();
|
||||
@@ -46,11 +49,11 @@ public abstract class ThemeValue<T> implements Comparable<ThemeValue<T>> {
|
||||
return value;
|
||||
}
|
||||
|
||||
T get(GThemeValueMap preferredValues) {
|
||||
if (cachedValue == null) {
|
||||
cachedValue = doGetValue(preferredValues);
|
||||
}
|
||||
return cachedValue;
|
||||
public T get(GThemeValueMap preferredValues) {
|
||||
// if (cachedValue == null) {
|
||||
return doGetValue(preferredValues);
|
||||
// }
|
||||
// return cachedValue;
|
||||
}
|
||||
|
||||
private T doGetValue(GThemeValueMap values) {
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package docking.theme.builtin;
|
||||
|
||||
import docking.theme.DiscoverableGTheme;
|
||||
import ghidra.docking.util.LookAndFeelUtils;
|
||||
|
||||
public class FlatDarculaTheme extends DiscoverableGTheme {
|
||||
public FlatDarculaTheme() {
|
||||
super("Flat Darcula", LookAndFeelUtils.FLAT_DARCULA_LOOK_AND_FEEL, true);
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,6 @@ import ghidra.docking.util.LookAndFeelUtils;
|
||||
|
||||
public class FlatDarkTheme extends DiscoverableGTheme {
|
||||
public FlatDarkTheme() {
|
||||
super("Dark Flat Theme", LookAndFeelUtils.FLAT_DARK_LOOK_AND_FEEL, true);
|
||||
super("Flat Dark", LookAndFeelUtils.FLAT_DARK_LOOK_AND_FEEL, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ import ghidra.docking.util.LookAndFeelUtils;
|
||||
public class FlatLightTheme extends DiscoverableGTheme {
|
||||
|
||||
public FlatLightTheme() {
|
||||
super("Flat", LookAndFeelUtils.FLAT_LIGHT_LOOK_AND_FEEL);
|
||||
super("Flat Light", LookAndFeelUtils.FLAT_LIGHT_LOOK_AND_FEEL);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package docking.theme.gui;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.ChangeListener;
|
||||
|
||||
import docking.DialogComponentProvider;
|
||||
import docking.DockingWindowManager;
|
||||
import docking.options.editor.GhidraColorChooser;
|
||||
import docking.theme.ColorValue;
|
||||
import docking.theme.Gui;
|
||||
import ghidra.util.Swing;
|
||||
|
||||
public class GThemeColorEditorDialog extends DialogComponentProvider {
|
||||
|
||||
private ColorValue originalColorValue;
|
||||
private ColorValue currentColorValue;
|
||||
|
||||
private GThemeDialog themeDialog;
|
||||
private GhidraColorChooser colorChooser;
|
||||
private ChangeListener colorChangeListener = e -> colorChanged();
|
||||
|
||||
public GThemeColorEditorDialog(GThemeDialog themeDialog) {
|
||||
super("Theme Color Editor", false);
|
||||
this.themeDialog = themeDialog;
|
||||
addWorkPanel(buildColorPanel());
|
||||
addOKButton();
|
||||
addCancelButton();
|
||||
}
|
||||
|
||||
public void editColor(ColorValue colorValue) {
|
||||
if (currentColorValue != null && !currentColorValue.equals(originalColorValue)) {
|
||||
themeDialog.colorChangeAccepted();
|
||||
}
|
||||
this.originalColorValue = colorValue;
|
||||
this.currentColorValue = colorValue;
|
||||
|
||||
setTitle("Edit Color For: " + colorValue.getId());
|
||||
Color color = Gui.getRawColor(originalColorValue.getId());
|
||||
colorChooser.getSelectionModel().removeChangeListener(colorChangeListener);
|
||||
colorChooser.setColor(color);
|
||||
colorChooser.getSelectionModel().addChangeListener(colorChangeListener);
|
||||
|
||||
if (!isShowing()) {
|
||||
DockingWindowManager.showDialog(themeDialog.getComponent(), this);
|
||||
}
|
||||
}
|
||||
|
||||
private JComponent buildColorPanel() {
|
||||
JPanel panel = new JPanel(new BorderLayout());
|
||||
panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
|
||||
colorChooser = new GhidraColorChooser();
|
||||
panel.add(colorChooser);
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void okCallback() {
|
||||
close();
|
||||
if (!currentColorValue.equals(originalColorValue)) {
|
||||
themeDialog.colorChangeAccepted();
|
||||
}
|
||||
currentColorValue = null;
|
||||
originalColorValue = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void cancelCallback() {
|
||||
retoreOriginalColor();
|
||||
close();
|
||||
currentColorValue = null;
|
||||
originalColorValue = null;
|
||||
}
|
||||
|
||||
private void retoreOriginalColor() {
|
||||
Gui.setColor(originalColorValue);
|
||||
}
|
||||
|
||||
private void colorChanged() {
|
||||
Color newColor = colorChooser.getColor();
|
||||
currentColorValue = new ColorValue(originalColorValue.getId(), newColor);
|
||||
Swing.runLater(() -> Gui.setColor(currentColorValue));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package docking.theme.gui;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import docking.DialogComponentProvider;
|
||||
import docking.theme.ColorValue;
|
||||
import docking.theme.Gui;
|
||||
import docking.widgets.combobox.GhidraComboBox;
|
||||
import docking.widgets.table.GFilterTable;
|
||||
import docking.widgets.table.GTable;
|
||||
import ghidra.util.Swing;
|
||||
import resources.Icons;
|
||||
|
||||
public class GThemeDialog extends DialogComponentProvider {
|
||||
|
||||
private ThemeColorTableModel colorTableModel;
|
||||
private GThemeColorEditorDialog dialog;
|
||||
|
||||
public GThemeDialog() {
|
||||
super("Theme Dialog", false);
|
||||
addWorkPanel(createMainPanel());
|
||||
addOKButton();
|
||||
addCancelButton();
|
||||
setOkButtonText("Save");
|
||||
setPreferredSize(1100, 500);
|
||||
setRememberSize(false);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void okCallback() {
|
||||
for (Window window : Window.getWindows()) {
|
||||
SwingUtilities.updateComponentTreeUI(window);
|
||||
}
|
||||
|
||||
// GhidraFileChooser chooser = new GhidraFileChooser(getComponent());
|
||||
// chooser.setTitle("Choose Theme File");
|
||||
// chooser.setApproveButtonText("Select Output File");
|
||||
// chooser.setApproveButtonToolTipText("Select File");
|
||||
// chooser.setFileSelectionMode(GhidraFileChooserMode.FILES_ONLY);
|
||||
// chooser.setSelectedFileFilter(GhidraFileFilter.ALL);
|
||||
// File file = chooser.getSelectedFile();
|
||||
// try {
|
||||
// Gui.getActiveTheme().saveToFile(file, Gui.getAllDefaultValues());
|
||||
// }
|
||||
// catch (IOException e) {
|
||||
// // TODO Auto-generated catch block
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
}
|
||||
|
||||
private JComponent createMainPanel() {
|
||||
JPanel panel = new JPanel();
|
||||
|
||||
panel.setLayout(new BorderLayout());
|
||||
panel.add(buildControlPanel(), BorderLayout.NORTH);
|
||||
panel.add(buildTabedTables());
|
||||
return panel;
|
||||
}
|
||||
|
||||
private Component buildControlPanel() {
|
||||
JPanel panel = new JPanel(new BorderLayout());
|
||||
panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
|
||||
// panel.add(buildThemeChoiceButtons(), BorderLayout.WEST);
|
||||
panel.add(buildThemeCombo(), BorderLayout.WEST);
|
||||
panel.add(buildReloadDefaultsButton(), BorderLayout.EAST);
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
private Component buildReloadDefaultsButton() {
|
||||
JButton button = new JButton(Icons.REFRESH_ICON);
|
||||
button.addActionListener(this::reloadThemeDefaults);
|
||||
button.setToolTipText("Reload Theme Defaults");
|
||||
return button;
|
||||
}
|
||||
|
||||
private Component buildThemeCombo() {
|
||||
JPanel panel = new JPanel();
|
||||
GhidraComboBox<String> combo = new GhidraComboBox<>(Gui.getAllThemeNames());
|
||||
combo.setSelectedItem(Gui.getActiveTheme().getName());
|
||||
combo.addItemListener(this::themeComboChanged);
|
||||
|
||||
panel.add(new JLabel("Theme: "), BorderLayout.WEST);
|
||||
panel.add(combo);
|
||||
panel.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 10));
|
||||
return panel;
|
||||
}
|
||||
|
||||
private Component buildThemeChoiceButtons() {
|
||||
JPanel panel = new JPanel(new FlowLayout());
|
||||
panel.add(createThemeButton("Flat"));
|
||||
panel.add(createThemeButton("Dark Flat"));
|
||||
panel.add(createThemeButton("Metal"));
|
||||
panel.add(createThemeButton("Nimbus"));
|
||||
panel.add(createThemeButton("GDK+"));
|
||||
panel.add(createThemeButton("CDE/Motif"));
|
||||
return panel;
|
||||
}
|
||||
|
||||
private JButton createThemeButton(String name) {
|
||||
JButton button = new JButton(name);
|
||||
button.addActionListener(e -> Gui.setTheme(Gui.getTheme(name)));
|
||||
return button;
|
||||
}
|
||||
|
||||
private Component buildTabedTables() {
|
||||
JTabbedPane tabbedPane = new JTabbedPane();
|
||||
tabbedPane.add("Colors", buildColorTable());
|
||||
return tabbedPane;
|
||||
}
|
||||
|
||||
private JComponent buildColorTable() {
|
||||
colorTableModel = new ThemeColorTableModel();
|
||||
|
||||
GFilterTable<ColorValue> filterTable = new GFilterTable<>(colorTableModel);
|
||||
GTable table = filterTable.getTable();
|
||||
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
|
||||
table.addKeyListener(new KeyAdapter() {
|
||||
@Override
|
||||
public void keyPressed(KeyEvent e) {
|
||||
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
|
||||
ColorValue colorValue = filterTable.getSelectedRowObject();
|
||||
if (colorValue != null) {
|
||||
editColor(colorValue);
|
||||
}
|
||||
e.consume();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
table.addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
if (e.getClickCount() == 2) {
|
||||
ColorValue value = filterTable.getItemAt(e.getPoint());
|
||||
editColor(value);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return filterTable;
|
||||
}
|
||||
|
||||
private void themeComboChanged(ItemEvent e) {
|
||||
if (e.getStateChange() == ItemEvent.SELECTED) {
|
||||
String themeName = (String) e.getItem();
|
||||
Swing.runLater(() -> Gui.setTheme(Gui.getTheme(themeName)));
|
||||
Swing.runLater(() -> colorTableModel.reload());
|
||||
}
|
||||
}
|
||||
|
||||
private void reloadThemeDefaults(ActionEvent e) {
|
||||
Gui.reloadThemeDefaults();
|
||||
colorTableModel.reload();
|
||||
}
|
||||
|
||||
protected void editColor(ColorValue value) {
|
||||
if (dialog == null) {
|
||||
dialog = new GThemeColorEditorDialog(this);
|
||||
}
|
||||
dialog.editColor(value);
|
||||
}
|
||||
|
||||
void colorChangeAccepted() {
|
||||
colorTableModel.reload();
|
||||
}
|
||||
|
||||
}
|
||||
+79
-26
@@ -13,36 +13,48 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package docking.theme;
|
||||
package docking.theme.gui;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.JLabel;
|
||||
|
||||
import docking.theme.*;
|
||||
import docking.widgets.table.*;
|
||||
import ghidra.docking.settings.Settings;
|
||||
import ghidra.framework.plugintool.ServiceProvider;
|
||||
import ghidra.framework.plugintool.ServiceProviderStub;
|
||||
import ghidra.util.ColorUtils;
|
||||
import ghidra.util.WebColors;
|
||||
import ghidra.util.table.column.AbstractGColumnRenderer;
|
||||
import ghidra.util.table.column.GColumnRenderer;
|
||||
|
||||
public class ThemeColorTableModel extends GDynamicColumnTableModel<ColorValue, Object> {
|
||||
private List<ColorValue> colors;
|
||||
private GThemeValueMap values;
|
||||
private GThemeValueMap coreDefaults;
|
||||
private GThemeValueMap darkDefaults;
|
||||
|
||||
public ThemeColorTableModel(GTheme theme) {
|
||||
public ThemeColorTableModel() {
|
||||
super(new ServiceProviderStub());
|
||||
colors = Gui.getAllValues().getColors();
|
||||
loadValues();
|
||||
}
|
||||
|
||||
public void refresh() {
|
||||
colors = Gui.getAllValues().getColors();
|
||||
public void reload() {
|
||||
loadValues();
|
||||
fireTableDataChanged();
|
||||
}
|
||||
|
||||
private void loadValues() {
|
||||
values = Gui.getAllValues();
|
||||
coreDefaults = Gui.getCoreDefaults();
|
||||
darkDefaults = Gui.getDarkDefaults();
|
||||
colors = values.getColors();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Users";
|
||||
@@ -53,18 +65,14 @@ public class ThemeColorTableModel extends GDynamicColumnTableModel<ColorValue, O
|
||||
return colors;
|
||||
}
|
||||
|
||||
public boolean isCellEditable(int row, int column) {
|
||||
return getColumnName(column).equals("Current Color");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TableColumnDescriptor<ColorValue> createTableColumnDescriptor() {
|
||||
TableColumnDescriptor<ColorValue> descriptor = new TableColumnDescriptor<>();
|
||||
descriptor.addVisibleColumn(new IdColumn());
|
||||
descriptor.addVisibleColumn(new ValueColumn("Current Color", () -> values));
|
||||
descriptor.addVisibleColumn(new ValueColumn("Core Defaults", () -> coreDefaults));
|
||||
descriptor.addVisibleColumn(new ValueColumn("Dark Defaults", () -> darkDefaults));
|
||||
descriptor.addVisibleColumn(new IsLafPropertyColumn());
|
||||
descriptor.addVisibleColumn(new ValueColumn("Current Color", Gui.getAllValues()));
|
||||
descriptor.addVisibleColumn(new ValueColumn("Core Defaults", Gui.getAllValues()));
|
||||
descriptor.addVisibleColumn(new ValueColumn("Dark Defaults", Gui.getDarkDefaults()));
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
@@ -85,17 +93,22 @@ public class ThemeColorTableModel extends GDynamicColumnTableModel<ColorValue, O
|
||||
ServiceProvider provider) throws IllegalArgumentException {
|
||||
return themeColor.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnPreferredWidth() {
|
||||
return 300;
|
||||
}
|
||||
}
|
||||
|
||||
class ValueColumn extends AbstractDynamicTableColumn<ColorValue, ColorValue, Object> {
|
||||
private ThemeColorRenderer renderer = new ThemeColorRenderer(Gui.getAllValues());
|
||||
private GThemeValueMap valueMap;
|
||||
private ThemeColorRenderer renderer;
|
||||
private String name;
|
||||
private Supplier<GThemeValueMap> valueSupplier;
|
||||
|
||||
ValueColumn(String name, GThemeValueMap valueMap) {
|
||||
ValueColumn(String name, Supplier<GThemeValueMap> supplier) {
|
||||
this.name = name;
|
||||
this.valueMap = valueMap;
|
||||
renderer = new ThemeColorRenderer(valueMap);
|
||||
this.valueSupplier = supplier;
|
||||
renderer = new ThemeColorRenderer(supplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -115,10 +128,15 @@ public class ThemeColorTableModel extends GDynamicColumnTableModel<ColorValue, O
|
||||
}
|
||||
|
||||
public Comparator<ColorValue> getComparator() {
|
||||
return (v1, v2) -> valueMap.getColor(v1.getId())
|
||||
.compareValue(valueMap.getColor(v2.getId()));
|
||||
return (v1, v2) -> valueSupplier.get()
|
||||
.getColor(v1.getId())
|
||||
.compareValue(valueSupplier.get().getColor(v2.getId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnPreferredWidth() {
|
||||
return 300;
|
||||
}
|
||||
}
|
||||
|
||||
class IsLafPropertyColumn extends AbstractDynamicTableColumn<ColorValue, Boolean, Object> {
|
||||
@@ -133,20 +151,25 @@ public class ThemeColorTableModel extends GDynamicColumnTableModel<ColorValue, O
|
||||
ServiceProvider provider) throws IllegalArgumentException {
|
||||
return Gui.isJavaDefinedColor(themeColor.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnPreferredWidth() {
|
||||
return 20;
|
||||
}
|
||||
}
|
||||
|
||||
private class ThemeColorRenderer extends AbstractGColumnRenderer<ColorValue> {
|
||||
|
||||
private GThemeValueMap valueMap;
|
||||
private Supplier<GThemeValueMap> mapSupplier;
|
||||
|
||||
public ThemeColorRenderer(GThemeValueMap valueMap) {
|
||||
this.valueMap = valueMap;
|
||||
public ThemeColorRenderer(Supplier<GThemeValueMap> mapSupplier) {
|
||||
this.mapSupplier = mapSupplier;
|
||||
setFont(new Font("Monospaced", Font.PLAIN, 12));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getTableCellRendererComponent(GTableCellRenderingData data) {
|
||||
|
||||
GThemeValueMap valueMap = mapSupplier.get();
|
||||
JLabel label = (JLabel) super.getTableCellRendererComponent(data);
|
||||
String id = ((ColorValue) data.getValue()).getId();
|
||||
|
||||
@@ -172,8 +195,9 @@ public class ThemeColorTableModel extends GDynamicColumnTableModel<ColorValue, O
|
||||
text = "<No Value>";
|
||||
}
|
||||
label.setText(text);
|
||||
label.setBackground(color);
|
||||
label.setForeground(ColorUtils.contrastForegroundColor(color));
|
||||
label.setIcon(new SwatchIcon(color, label.getForeground()));
|
||||
// label.setBackground(color);
|
||||
// label.setForeground(ColorUtils.contrastForegroundColor(color));
|
||||
label.setOpaque(true);
|
||||
return label;
|
||||
}
|
||||
@@ -184,4 +208,33 @@ public class ThemeColorTableModel extends GDynamicColumnTableModel<ColorValue, O
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class SwatchIcon implements Icon {
|
||||
private Color color;
|
||||
private Color border;
|
||||
|
||||
SwatchIcon(Color c, Color border) {
|
||||
this.color = c;
|
||||
this.border = border;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paintIcon(Component c, Graphics g, int x, int y) {
|
||||
g.setColor(color);
|
||||
g.fillRect(x, y, 25, 16);
|
||||
g.setColor(border);
|
||||
g.drawRect(x, y, 25, 16);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIconWidth() {
|
||||
return 25;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIconHeight() {
|
||||
return 16;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package docking.theme.laf;
|
||||
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.UnsupportedLookAndFeelException;
|
||||
|
||||
import com.formdev.flatlaf.FlatLaf;
|
||||
|
||||
public class FlatLookAndFeelInstaller extends LookAndFeelInstaller {
|
||||
private FlatLaf lookAndFeel;
|
||||
|
||||
public FlatLookAndFeelInstaller(FlatLaf lookAndFeel) {
|
||||
this.lookAndFeel = lookAndFeel;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void installLookAndFeel() throws UnsupportedLookAndFeelException {
|
||||
UIManager.setLookAndFeel(lookAndFeel);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package docking.theme.laf;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.UIDefaults;
|
||||
import javax.swing.UnsupportedLookAndFeelException;
|
||||
import javax.swing.plaf.nimbus.NimbusLookAndFeel;
|
||||
|
||||
import docking.theme.*;
|
||||
import ghidra.docking.util.LookAndFeelUtils;
|
||||
|
||||
public class GDKLookAndFeelInstaller extends LookAndFeelInstaller {
|
||||
|
||||
@Override
|
||||
protected void installLookAndFeel() throws UnsupportedLookAndFeelException {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Extends the NimbusLookAndFeel to intercept the {@link #getDefaults()}. To get Nimbus
|
||||
* to use our indirect values, we have to get in early.
|
||||
*/
|
||||
static class ExtendedGDKKLookAndFeel extends NimbusLookAndFeel {
|
||||
|
||||
@Override
|
||||
public UIDefaults getDefaults() {
|
||||
GThemeValueMap javaDefaults = new GThemeValueMap();
|
||||
|
||||
UIDefaults defaults = super.getDefaults();
|
||||
List<String> colorIds =
|
||||
LookAndFeelUtils.getLookAndFeelIdsForType(defaults, Color.class);
|
||||
for (String id : colorIds) {
|
||||
Color color = defaults.getColor(id);
|
||||
ColorValue value = new ColorValue(id, color);
|
||||
javaDefaults.addColor(value);
|
||||
}
|
||||
Gui.setJavaDefaults(javaDefaults);
|
||||
for (String id : colorIds) {
|
||||
defaults.put(id, Gui.getGColorUiResource(id));
|
||||
}
|
||||
// javaDefaults.addColor(new ColorValue("Label.textForground", "Label.foreground"));
|
||||
defaults.put("Label.textForeground", Gui.getGColorUiResource("Label.foreground"));
|
||||
GColor.refreshAll();
|
||||
return defaults;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package docking.theme.laf;
|
||||
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.UIManager.LookAndFeelInfo;
|
||||
|
||||
import ghidra.docking.util.LookAndFeelUtils;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
public class GenericLookAndFeelInstaller extends LookAndFeelInstaller {
|
||||
private String name;
|
||||
|
||||
public GenericLookAndFeelInstaller(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void installLookAndFeel() throws Exception {
|
||||
String className = findLookAndFeelClassName(name);
|
||||
UIManager.setLookAndFeel(className);
|
||||
}
|
||||
|
||||
private static String findLookAndFeelClassName(String lookAndFeelName) {
|
||||
if (lookAndFeelName.equalsIgnoreCase(LookAndFeelUtils.SYSTEM_LOOK_AND_FEEL)) {
|
||||
return UIManager.getSystemLookAndFeelClassName();
|
||||
}
|
||||
|
||||
LookAndFeelInfo[] installedLookAndFeels = UIManager.getInstalledLookAndFeels();
|
||||
for (LookAndFeelInfo info : installedLookAndFeels) {
|
||||
String className = info.getClassName();
|
||||
if (lookAndFeelName.equals(className) || lookAndFeelName.equals(info.getName())) {
|
||||
return className;
|
||||
}
|
||||
}
|
||||
|
||||
Msg.debug(LookAndFeelUtils.class,
|
||||
"Unable to find requested Look and Feel: " + lookAndFeelName);
|
||||
return UIManager.getSystemLookAndFeelClassName();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package docking.theme.laf;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.UIDefaults;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.plaf.UIResource;
|
||||
|
||||
import docking.theme.*;
|
||||
import ghidra.docking.util.LookAndFeelUtils;
|
||||
|
||||
public abstract class LookAndFeelInstaller {
|
||||
|
||||
public void install() throws Exception {
|
||||
cleanUiDefaults();
|
||||
installLookAndFeel();
|
||||
installJavaDefaults();
|
||||
fixupLookAndFeelIssues();
|
||||
}
|
||||
|
||||
protected abstract void installLookAndFeel() throws Exception;
|
||||
|
||||
protected void fixupLookAndFeelIssues() {
|
||||
// no generic fix-ups at this time.
|
||||
}
|
||||
|
||||
protected void installJavaDefaults() {
|
||||
GThemeValueMap javaDefaults = extractJavaDefaults();
|
||||
Gui.setJavaDefaults(javaDefaults);
|
||||
installIndirectValues(javaDefaults);
|
||||
}
|
||||
|
||||
private void installIndirectValues(GThemeValueMap javaDefaults) {
|
||||
UIDefaults defaults = UIManager.getDefaults();
|
||||
for (ColorValue colorValue : javaDefaults.getColors()) {
|
||||
String id = colorValue.getId();
|
||||
GColorUIResource gColor = Gui.getGColorUiResource(id);
|
||||
defaults.put(id, gColor);
|
||||
}
|
||||
for (FontValue fontValue : javaDefaults.getFonts()) {
|
||||
String id = fontValue.getId();
|
||||
GFont gFont = new GFont(id);
|
||||
if (!gFont.equals(fontValue.getRawValue())) {
|
||||
// only update if we have changed the default java color
|
||||
defaults.put(id, gFont);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected GThemeValueMap extractJavaDefaults() {
|
||||
GThemeValueMap values = new GThemeValueMap();
|
||||
// for now, just doing color properties.
|
||||
List<String> ids =
|
||||
LookAndFeelUtils.getLookAndFeelIdsForType(UIManager.getDefaults(), Color.class);
|
||||
for (String id : ids) {
|
||||
values.addColor(new ColorValue(id, getNonUiColor(id)));
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
private static Color getNonUiColor(String id) {
|
||||
// Not sure, but for now, make sure colors are not UIResource
|
||||
Color color = UIManager.getColor(id);
|
||||
if (color instanceof UIResource) {
|
||||
return new Color(color.getRGB(), true);
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
||||
private void cleanUiDefaults() {
|
||||
GThemeValueMap javaDefaults = Gui.getJavaDefaults();
|
||||
if (javaDefaults == null) {
|
||||
return;
|
||||
}
|
||||
UIDefaults defaults = UIManager.getDefaults();
|
||||
for (ColorValue colorValue : javaDefaults.getColors()) {
|
||||
String id = colorValue.getId();
|
||||
defaults.put(id, null);
|
||||
}
|
||||
// for (FontValue fontValue : javaDefaults.getFonts()) {
|
||||
// String id = fontValue.getId();
|
||||
// defaults.put(id, null);
|
||||
// }
|
||||
}
|
||||
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package docking.theme.laf;
|
||||
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.UnsupportedLookAndFeelException;
|
||||
import javax.swing.plaf.metal.MetalLookAndFeel;
|
||||
|
||||
public class MetalLookAndFeelInstaller extends LookAndFeelInstaller {
|
||||
|
||||
@Override
|
||||
protected void installLookAndFeel() throws UnsupportedLookAndFeelException {
|
||||
UIManager.setLookAndFeel(new MetalLookAndFeel());
|
||||
}
|
||||
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package docking.theme.laf;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.plaf.nimbus.NimbusLookAndFeel;
|
||||
|
||||
import docking.theme.*;
|
||||
import ghidra.docking.util.LookAndFeelUtils;
|
||||
|
||||
public class NimbusLookAndFeelInstaller extends LookAndFeelInstaller {
|
||||
|
||||
@Override
|
||||
protected void installLookAndFeel() throws UnsupportedLookAndFeelException {
|
||||
UIManager.setLookAndFeel(new GNimbusLookAndFeel());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void installJavaDefaults() {
|
||||
// do nothing - already handled by extended NimbusLookAndFeel
|
||||
}
|
||||
|
||||
/**
|
||||
* Extends the NimbusLookAndFeel to intercept the {@link #getDefaults()}. To get Nimbus
|
||||
* to use our indirect values, we have to get in early.
|
||||
*/
|
||||
static class GNimbusLookAndFeel extends NimbusLookAndFeel {
|
||||
|
||||
@Override
|
||||
public UIDefaults getDefaults() {
|
||||
GThemeValueMap javaDefaults = new GThemeValueMap();
|
||||
|
||||
UIDefaults defaults = super.getDefaults();
|
||||
List<String> colorIds =
|
||||
LookAndFeelUtils.getLookAndFeelIdsForType(defaults, Color.class);
|
||||
for (String id : colorIds) {
|
||||
Color color = defaults.getColor(id);
|
||||
ColorValue value = new ColorValue(id, color);
|
||||
javaDefaults.addColor(value);
|
||||
}
|
||||
Gui.setJavaDefaults(javaDefaults);
|
||||
for (String id : colorIds) {
|
||||
defaults.put(id, Gui.getGColorUiResource(id));
|
||||
}
|
||||
// javaDefaults.addColor(new ColorValue("Label.textForground", "Label.foreground"));
|
||||
defaults.put("Label.textForeground", Gui.getGColorUiResource("Label.foreground"));
|
||||
GColor.refreshAll();
|
||||
return defaults;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+5
-3
@@ -22,8 +22,7 @@ import java.text.DecimalFormat;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.SwingConstants;
|
||||
import javax.swing.*;
|
||||
import javax.swing.table.TableCellRenderer;
|
||||
import javax.swing.table.TableModel;
|
||||
|
||||
@@ -98,7 +97,10 @@ public class GTableCellRenderer extends AbstractGCellRenderer implements TableCe
|
||||
"Using a GTableCellRenderer in a non-GTable table. (Model class: " +
|
||||
table.getModel().getClass().getName() + ")");
|
||||
}
|
||||
|
||||
// check if LookAndFeel has changed
|
||||
if (UIManager.getUI(this) != getUI()) {
|
||||
updateUI();
|
||||
}
|
||||
GTable gTable = (GTable) table;
|
||||
GTableCellRenderingData data = gTable.getRenderingData(column);
|
||||
Object rowObject = null;
|
||||
|
||||
+5
-5
@@ -22,6 +22,7 @@ import javax.swing.JTree;
|
||||
import javax.swing.plaf.ColorUIResource;
|
||||
import javax.swing.tree.DefaultTreeCellRenderer;
|
||||
|
||||
import docking.theme.GColor;
|
||||
import docking.widgets.GComponent;
|
||||
import docking.widgets.tree.GTree;
|
||||
import docking.widgets.tree.GTreeNode;
|
||||
@@ -53,7 +54,6 @@ public class GTreeRenderer extends DefaultTreeCellRenderer implements GComponent
|
||||
paintDropTarget = (value == dropTarget);
|
||||
|
||||
setBackground(selected1 ? getBackgroundSelectionColor() : getBackgroundNonSelectionColor());
|
||||
|
||||
if (!(value instanceof GTreeNode)) {
|
||||
// not a GTree
|
||||
return this;
|
||||
@@ -84,12 +84,12 @@ public class GTreeRenderer extends DefaultTreeCellRenderer implements GComponent
|
||||
|
||||
@Override
|
||||
public void setBackgroundSelectionColor(Color newColor) {
|
||||
super.setBackgroundSelectionColor(fromUiResource(newColor));
|
||||
super.setBackgroundSelectionColor(fromUiResource(newColor, "Tree.selectionBackground"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBackgroundNonSelectionColor(Color newColor) {
|
||||
super.setBackgroundNonSelectionColor(fromUiResource(newColor));
|
||||
super.setBackgroundNonSelectionColor(fromUiResource(newColor, "Tree.textBackground"));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -101,9 +101,9 @@ public class GTreeRenderer extends DefaultTreeCellRenderer implements GComponent
|
||||
* @param c the source color
|
||||
* @return the new color
|
||||
*/
|
||||
protected Color fromUiResource(Color c) {
|
||||
protected Color fromUiResource(Color c, String defaultKey) {
|
||||
if (c instanceof ColorUIResource) {
|
||||
return new Color(c.getRGB());
|
||||
return new GColor(defaultKey);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,11 @@ import javax.swing.*;
|
||||
import javax.swing.UIManager.LookAndFeelInfo;
|
||||
import javax.swing.plaf.ComponentUI;
|
||||
|
||||
import org.apache.commons.collections4.IteratorUtils;
|
||||
|
||||
import com.formdev.flatlaf.*;
|
||||
|
||||
import docking.theme.laf.*;
|
||||
import ghidra.framework.OperatingSystem;
|
||||
import ghidra.framework.Platform;
|
||||
import ghidra.framework.preferences.Preferences;
|
||||
@@ -63,6 +68,11 @@ public class LookAndFeelUtils {
|
||||
* The Flatlaf implementation of dark mode.
|
||||
*/
|
||||
public static final String FLAT_DARK_LOOK_AND_FEEL = "Flat Dark";
|
||||
/**
|
||||
* The Flatlaf implementation of darcula mode.
|
||||
*/
|
||||
public static final String FLAT_DARCULA_LOOK_AND_FEEL = "Flat Darcula";
|
||||
|
||||
public static final String WINDOWS = "Windows";
|
||||
public static final String WINDOWS_CLASSIC = "Windows Classic";
|
||||
|
||||
@@ -118,14 +128,32 @@ public class LookAndFeelUtils {
|
||||
installPopupMenuSettingsOverride();
|
||||
}
|
||||
catch (Exception exc) {
|
||||
Msg.error(LookAndFeelUtils.class,
|
||||
"Error loading Look and Feel: " + exc, exc);
|
||||
Msg.error(LookAndFeelUtils.class, "Error loading Look and Feel: " + exc, exc);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void setLookAndFeel(LookAndFeel laf) {
|
||||
SystemUtilities.runSwingNow(() -> {
|
||||
try {
|
||||
UIManager.setLookAndFeel(laf);
|
||||
fixupLookAndFeelIssues();
|
||||
|
||||
// some custom values for any given LAF
|
||||
installGlobalLookAndFeelAttributes();
|
||||
installGlobalFontSizeOverride();
|
||||
installCustomLookAndFeelActions();
|
||||
installPopupMenuSettingsOverride();
|
||||
}
|
||||
catch (Exception exc) {
|
||||
Msg.error(LookAndFeelUtils.class, "Error loading Look and Feel: " + exc, exc);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public static void dumpUIProperties() {
|
||||
List<String> colorKeys = getLookAndFeelIdsForType(Color.class);
|
||||
List<String> colorKeys = getLookAndFeelIdsForType(UIManager.getDefaults(), Color.class);
|
||||
Collections.sort(colorKeys);
|
||||
for (String string : colorKeys) {
|
||||
Msg.debug(LookAndFeelUtils.class, string + "\t\t" + UIManager.get(string));
|
||||
@@ -133,13 +161,12 @@ public class LookAndFeelUtils {
|
||||
|
||||
}
|
||||
|
||||
public static List<String> getLookAndFeelIdsForType(Class<?> clazz) {
|
||||
UIDefaults defaults = UIManager.getDefaults();
|
||||
public static List<String> getLookAndFeelIdsForType(UIDefaults defaults, Class<?> clazz) {
|
||||
List<String> colorKeys = new ArrayList<>();
|
||||
for (Entry<?, ?> entry : defaults.entrySet()) {
|
||||
Object key = entry.getKey();
|
||||
List<Object> keyList = IteratorUtils.toList(defaults.keys().asIterator());
|
||||
for (Object key : keyList) {
|
||||
if (key instanceof String) {
|
||||
Object value = entry.getValue();
|
||||
Object value = defaults.get(key);
|
||||
if (clazz.isInstance(value)) {
|
||||
colorKeys.add((String) key);
|
||||
}
|
||||
@@ -165,37 +192,28 @@ public class LookAndFeelUtils {
|
||||
return list;
|
||||
}
|
||||
|
||||
private static void installLookAndFeelByName(String lookAndFeelName)
|
||||
throws ClassNotFoundException, InstantiationException, IllegalAccessException,
|
||||
UnsupportedLookAndFeelException {
|
||||
private static void installLookAndFeelByName(String lookAndFeelName) throws Exception {
|
||||
|
||||
String lookAndFeelClassName = findLookAndFeelClassName(lookAndFeelName);
|
||||
UIManager.setLookAndFeel(lookAndFeelClassName);
|
||||
LookAndFeelInstaller installer = getLookAndFeelInstaller(lookAndFeelName);
|
||||
installer.install();
|
||||
fixupLookAndFeelIssues();
|
||||
}
|
||||
|
||||
private static String findLookAndFeelClassName(String lookAndFeelName) {
|
||||
if (lookAndFeelName.equalsIgnoreCase(SYSTEM_LOOK_AND_FEEL)) {
|
||||
return UIManager.getSystemLookAndFeelClassName();
|
||||
private static LookAndFeelInstaller getLookAndFeelInstaller(String lookAndFeelName) {
|
||||
switch (lookAndFeelName) {
|
||||
case NIMBUS_LOOK_AND_FEEL:
|
||||
return new NimbusLookAndFeelInstaller();
|
||||
case METAL_LOOK_AND_FEEL:
|
||||
return new MetalLookAndFeelInstaller();
|
||||
case FLAT_LIGHT_LOOK_AND_FEEL:
|
||||
return new FlatLookAndFeelInstaller(new FlatLightLaf());
|
||||
case FLAT_DARK_LOOK_AND_FEEL:
|
||||
return new FlatLookAndFeelInstaller(new FlatDarkLaf());
|
||||
case FLAT_DARCULA_LOOK_AND_FEEL:
|
||||
return new FlatLookAndFeelInstaller(new FlatDarculaLaf());
|
||||
default:
|
||||
return new GenericLookAndFeelInstaller(lookAndFeelName);
|
||||
}
|
||||
else if (lookAndFeelName.equalsIgnoreCase(FLAT_LIGHT_LOOK_AND_FEEL)) {
|
||||
return "com.formdev.flatlaf.FlatLightLaf";
|
||||
}
|
||||
else if (lookAndFeelName.equalsIgnoreCase(FLAT_DARK_LOOK_AND_FEEL)) {
|
||||
return "com.formdev.flatlaf.FlatDarkLaf";
|
||||
}
|
||||
|
||||
LookAndFeelInfo[] installedLookAndFeels = UIManager.getInstalledLookAndFeels();
|
||||
for (LookAndFeelInfo info : installedLookAndFeels) {
|
||||
String className = info.getClassName();
|
||||
if (lookAndFeelName.equals(className) || lookAndFeelName.equals(info.getName())) {
|
||||
return className;
|
||||
}
|
||||
}
|
||||
|
||||
Msg.debug(LookAndFeelUtils.class,
|
||||
"Unable to find requested Look and Feel: " + lookAndFeelName);
|
||||
return UIManager.getSystemLookAndFeelClassName();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -368,4 +386,28 @@ public class LookAndFeelUtils {
|
||||
return NIMBUS_LOOK_AND_FEEL.equals(lookAndFeel.getName());
|
||||
}
|
||||
|
||||
private static String findLookAndFeelClassName(String lookAndFeelName) {
|
||||
if (lookAndFeelName.equalsIgnoreCase(SYSTEM_LOOK_AND_FEEL)) {
|
||||
return UIManager.getSystemLookAndFeelClassName();
|
||||
}
|
||||
else if (lookAndFeelName.equalsIgnoreCase(FLAT_LIGHT_LOOK_AND_FEEL)) {
|
||||
return "com.formdev.flatlaf.FlatLightLaf";
|
||||
}
|
||||
else if (lookAndFeelName.equalsIgnoreCase(FLAT_DARK_LOOK_AND_FEEL)) {
|
||||
return "com.formdev.flatlaf.FlatDarkLaf";
|
||||
}
|
||||
|
||||
LookAndFeelInfo[] installedLookAndFeels = UIManager.getInstalledLookAndFeels();
|
||||
for (LookAndFeelInfo info : installedLookAndFeels) {
|
||||
String className = info.getClassName();
|
||||
if (lookAndFeelName.equals(className) || lookAndFeelName.equals(info.getName())) {
|
||||
return className;
|
||||
}
|
||||
}
|
||||
|
||||
Msg.debug(LookAndFeelUtils.class,
|
||||
"Unable to find requested Look and Feel: " + lookAndFeelName);
|
||||
return UIManager.getSystemLookAndFeelClassName();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ public class GuiTest extends AbstractDockingTest {
|
||||
public void setUp() {
|
||||
Gui.setPropertiesLoader(new ThemePropertiesLoader() {
|
||||
@Override
|
||||
public void initialize() {
|
||||
public void load() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
|
||||
@@ -382,11 +382,19 @@ public abstract class WebColors {
|
||||
int red = Integer.parseInt(split[0]);
|
||||
int green = Integer.parseInt(split[1]);
|
||||
int blue = Integer.parseInt(split[2]);
|
||||
float alpha = Float.parseFloat(split[3]);
|
||||
return new Color(red, green, blue, (int) (alpha * 255 + 0.5));
|
||||
int alpha = parseAlpha(split[3]);
|
||||
return new Color(red, green, blue, alpha);
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static int parseAlpha(String string) {
|
||||
if (string.contains(".")) {
|
||||
float value = Float.parseFloat(string);
|
||||
return (int) (value * 0xff + 0.5) & 0xff;
|
||||
}
|
||||
return Integer.parseInt(string) & 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
/* ###
|
||||
* 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.util.datastruct;
|
||||
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Class for storing weak reference to object instances. Objects of type T can be place
|
||||
* in this store and they will remain there until there are no references to that object
|
||||
* remaining. Note that this is not a Set and you can have multiple instances that are
|
||||
* "equal" in this store.The main purpose of this store is to be able to get all objects
|
||||
* in the store that are still reference; typically to refresh or "kick" them in some manner.
|
||||
* Could be useful for a thread safe weak listener list.
|
||||
*
|
||||
* @param <T> The type of objects stored in this WeakStore
|
||||
*/
|
||||
public class WeakStore<T> {
|
||||
protected ReferenceQueue<T> refQueue;
|
||||
private Link<T> first;
|
||||
private Link<T> last;
|
||||
private int size = 0;
|
||||
|
||||
public WeakStore() {
|
||||
refQueue = new ReferenceQueue<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of objects of type T remaining in the store. Those that are remaining
|
||||
* are either still referenced
|
||||
* @return the number of objects still in the store that haven't yet been garbage collected
|
||||
*/
|
||||
public synchronized int size() {
|
||||
processQueue();
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns a list of all the objects in this store
|
||||
* @return a list of all the objects in this store
|
||||
*/
|
||||
public synchronized List<T> getValues() {
|
||||
processQueue();
|
||||
List<T> values = new ArrayList<>();
|
||||
for (Link<T> l = first; l != null; l = l.nextLink) {
|
||||
T value = l.get();
|
||||
if (value != null) {
|
||||
values.add(value);
|
||||
}
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given value to the store
|
||||
* @param value the instance being added to the store
|
||||
*/
|
||||
public synchronized void add(T value) {
|
||||
Objects.requireNonNull(value);
|
||||
|
||||
processQueue();
|
||||
Link<T> newLink = new Link<>(last, value, null, refQueue);
|
||||
if (last == null) {
|
||||
first = newLink;
|
||||
}
|
||||
else {
|
||||
last.nextLink = newLink;
|
||||
}
|
||||
last = newLink;
|
||||
size++;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void processQueue() {
|
||||
Link<T> ref;
|
||||
while ((ref = (Link<T>) refQueue.poll()) != null) {
|
||||
remove(ref);
|
||||
}
|
||||
}
|
||||
|
||||
private void remove(Link<T> link) {
|
||||
if (link.previousLink == null) {
|
||||
first = link.nextLink;
|
||||
}
|
||||
else {
|
||||
link.previousLink.nextLink = link.nextLink;
|
||||
}
|
||||
if (link.nextLink == null) {
|
||||
last = link.previousLink;
|
||||
}
|
||||
else {
|
||||
link.nextLink.previousLink = link.previousLink;
|
||||
}
|
||||
size--;
|
||||
}
|
||||
|
||||
private static class Link<T> extends WeakReference<T> {
|
||||
private Link<T> nextLink;
|
||||
private Link<T> previousLink;
|
||||
|
||||
public Link(Link<T> previous, T value, Link<T> next, ReferenceQueue<T> refQueue) {
|
||||
super(value, refQueue);
|
||||
this.nextLink = next;
|
||||
this.previousLink = previous;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,7 +35,7 @@ public class WebColorsTest {
|
||||
|
||||
@Test
|
||||
public void testColorToStringFromColorWithNoDefinedEntry() {
|
||||
assertEquals("#0123EF", WebColors.toString(new Color(0x01, 0x23, 0xEF)));
|
||||
assertEquals("#0123ef", WebColors.toString(new Color(0x01, 0x23, 0xef)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -45,10 +45,20 @@ public class WebColorsTest {
|
||||
assertEquals(WebColors.NAVY, WebColors.getColor("#000080"));
|
||||
assertEquals(WebColors.NAVY, WebColors.getColor("rgb(0,0,128)"));
|
||||
assertEquals(WebColors.NAVY, WebColors.getColor("rgba(0,0,128,1.0)"));
|
||||
assertEquals(WebColors.NAVY, WebColors.getColor("rgba(0,0,128, 255)"));
|
||||
|
||||
assertEquals(new Color(0x123456), WebColors.getColor("0x123456"));
|
||||
assertEquals(new Color(0x80102030, true), WebColors.getColor("rgba(16, 32, 48, 0.5)"));
|
||||
|
||||
assertNull(WebColors.getColor("asdfasdfas"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testColorWithAlphaRoundTrip() {
|
||||
Color c = new Color(0x44112233, true);
|
||||
assertEquals(0x44, c.getAlpha());
|
||||
String string = WebColors.toString(c, false);
|
||||
Color parsed = WebColors.getColor(string);
|
||||
assertEquals(c, parsed);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/* ###
|
||||
* 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.util.datastruct;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class WeakStoreTest {
|
||||
@Test
|
||||
public void testStore() throws InterruptedException {
|
||||
WeakStore<Foo> store = new WeakStore<>();
|
||||
store.add(new Foo("AAA"));
|
||||
store.add(new Foo("BBB"));
|
||||
store.add(new Foo("CCC"));
|
||||
|
||||
assertEquals(3, store.size());
|
||||
|
||||
List<Foo> values = store.getValues();
|
||||
|
||||
assertEquals("AAA", values.get(0).getName());
|
||||
assertEquals("BBB", values.get(1).getName());
|
||||
assertEquals("CCC", values.get(2).getName());
|
||||
values = null;
|
||||
|
||||
for (int i = 0; i < 20; i++) {
|
||||
System.gc();
|
||||
if (store.size() == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
assertEquals(0, store.size());
|
||||
}
|
||||
|
||||
static class Foo {
|
||||
String name;
|
||||
|
||||
Foo(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user