mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 18:29:37 +02:00
Merge remote-tracking branch 'origin/patch'
This commit is contained in:
commit
fae64a82c0
45 changed files with 1087 additions and 445 deletions
|
@ -1188,13 +1188,11 @@ void CollapseStructure::orderLoopBodies(void)
|
|||
bool CollapseStructure::updateLoopBody(void)
|
||||
|
||||
{
|
||||
if (finaltrace) { // If we've already performed trace on DAG with no likely goto edges
|
||||
return false; // don't repeat the trace
|
||||
}
|
||||
FlowBlock *loopbottom = (FlowBlock *)0;
|
||||
FlowBlock *looptop = (FlowBlock *)0;
|
||||
if (finaltrace) { // If we've already performed the final trace
|
||||
if (likelyiter == likelygoto.end())
|
||||
return false; // We have nothing more to give
|
||||
return true;
|
||||
}
|
||||
while (loopbodyiter != loopbody.end()) { // Last innermost loop
|
||||
loopbottom = (*loopbodyiter).getCurrentBounds(&looptop,&graph);
|
||||
if (loopbottom != (FlowBlock *)0) {
|
||||
|
@ -1206,8 +1204,10 @@ bool CollapseStructure::updateLoopBody(void)
|
|||
likelylistfull = false; // Need to generate likely list for new loopbody (or no loopbody)
|
||||
loopbottom = (FlowBlock *)0;
|
||||
}
|
||||
if (likelylistfull) return true;
|
||||
// If we reach here, need to generate likely gotos for a new inner loop
|
||||
if (likelylistfull && likelyiter != likelygoto.end())
|
||||
return true;
|
||||
|
||||
// If we reach here, need to generate likely gotos for a new inner loop or DAG
|
||||
likelygoto.clear(); // Clear out any old likely gotos from last inner loop
|
||||
TraceDAG tracer(likelygoto);
|
||||
if (loopbottom != (FlowBlock *)0) {
|
||||
|
@ -1216,7 +1216,6 @@ bool CollapseStructure::updateLoopBody(void)
|
|||
(*loopbodyiter).setExitMarks(&graph); // Set the bounds of the TraceDAG
|
||||
}
|
||||
else {
|
||||
finaltrace = true;
|
||||
for(uint4 i=0;i<graph.getSize();++i) {
|
||||
FlowBlock *bl = graph.getBlock(i);
|
||||
if (bl->sizeIn() == 0)
|
||||
|
@ -1225,11 +1224,15 @@ bool CollapseStructure::updateLoopBody(void)
|
|||
}
|
||||
tracer.initialize();
|
||||
tracer.pushBranches();
|
||||
likelylistfull = true; // Mark likelygoto generation complete for current loop or DAG
|
||||
if (loopbottom != (FlowBlock *)0) {
|
||||
(*loopbodyiter).emitLikelyEdges(likelygoto,&graph);
|
||||
(*loopbodyiter).clearExitMarks(&graph);
|
||||
}
|
||||
likelylistfull = true;
|
||||
else if (likelygoto.empty()) {
|
||||
finaltrace = true; // No loops left and trace didn't find gotos
|
||||
return false;
|
||||
}
|
||||
likelyiter = likelygoto.begin();
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -333,10 +333,10 @@ color.fg.listing.bytes = orange
|
|||
|
||||
<P>
|
||||
Properties defined by the theming system do not follow this pattern. To reference a
|
||||
property that does not have a standard prefix, an ID can be prefixed with <COD>[color]
|
||||
property that does not have a standard prefix, an ID can be prefixed with <CODE>[color]
|
||||
</CODE>, <CODE>[font]</CODE>, or <CODE>[icon]</CODE> as appropriate to allow the theme
|
||||
property parser to recognize the values as IDs to other properties. For example, to refer to a
|
||||
system property named<CODE>system.color.bg.view</CODE>,
|
||||
system property named <CODE>system.color.bg.view</CODE>,
|
||||
you would use the following definition:
|
||||
<P>
|
||||
<BLOCKUOTE>
|
||||
|
@ -454,7 +454,7 @@ color.fg.listing.bytes = orange
|
|||
<BLOCKQUOTE>
|
||||
<PRE>
|
||||
<CODE>
|
||||
<I>iconName</I>[size(width,height)][disabled]{iconOverlayName[size(width,height)[disabled][move(x,y)]}{...}
|
||||
<I>iconName</I>[size(width,height)][disabled]{overlayIconName[size(width,height)[disabled][move(x,y)]}{...}
|
||||
|
||||
</CODE>
|
||||
</PRE>
|
||||
|
@ -522,7 +522,7 @@ color.fg.listing.bytes = orange
|
|||
|
||||
<BLOCKQUOTE>
|
||||
<P>
|
||||
A list of palette colors has been defined in <CODE>gui.palette.theme.properties.</CODE>.
|
||||
A list of palette colors has been defined in <CODE>gui.palette.theme.properties</CODE>.
|
||||
These palette colors values are meant to be used by developers to reduce the total number
|
||||
of colors used in the application. These color ids and values are viewable in the
|
||||
<A href="ThemingUserDocs.html#Edit_Theme">Theme Editor Dialog</A>.
|
||||
|
|
|
@ -38,8 +38,8 @@
|
|||
<CODE>ColorValue</CODE>, <CODE>FontValue</CODE>, and
|
||||
<CODE>IconValue</CODE>. Resource values are stored in these <CODE>ThemeValue</CODE> sub-classes
|
||||
because the value can be
|
||||
either a concrete value or be a reference to some other resource of the same type. So, for
|
||||
example, "color.bg.foo" could map directly to an actual color or its value could be reference
|
||||
either a concrete value or a reference to some other resource of the same type. So, for
|
||||
example, "color.bg.foo" could map directly to an actual color or its value could be a reference
|
||||
to some other indirect color like "color.bg.bar". In any <CODE>ThemeValue</CODE> object, either
|
||||
the referenced ID or the direct value must be null. To get the ultimate concrete value, there
|
||||
is a convenience method called <CODE>get()</CODE> on <CODE>ThemeValue</CODE>s that takes a
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
<HTML>
|
||||
<HEAD>
|
||||
<TITLE>General Overivew</TITLE>
|
||||
<TITLE>Theming Overivew</TITLE>
|
||||
<LINK rel="stylesheet" type="text/css" href="help/shared/DefaultStyle.css">
|
||||
</HEAD>
|
||||
|
||||
|
@ -86,7 +86,7 @@
|
|||
themes to choose from and by simply switching the theme, the system will then update the Look
|
||||
and Feel along with the colors, fonts, and icons, all with one action. The set of themes to
|
||||
choose from is a mix of built-in themes and saved custom themes. There is one built-in theme
|
||||
for each supported Look and Feel. The chosen theme will use the UI values defied by the Look
|
||||
for each supported Look and Feel. The chosen theme will use the UI values defined by the Look
|
||||
and Feel, as well as all the values for the defined property IDs. Users are able to
|
||||
create custom themes to change any color, font, or icon defined by the application, along with
|
||||
UI values supplied by the associated Look and Feel.</P>
|
||||
|
|
|
@ -233,9 +233,9 @@
|
|||
<BlOCKQUOTE>
|
||||
|
||||
<P>Theme Property Names (also referred to as IDs or keys) that are defined by the application
|
||||
use a common format to help make sorting and viewing properties more intuitive as to their use. See
|
||||
the <A href="ThemingDeveloperDocs.html#Resource_Ids">Developer Documentation</A> for more details on the property ID
|
||||
format and naming conventions.</P>
|
||||
use a common format to help make sorting and viewing properties more intuitive as to their use.
|
||||
See the <A href="ThemingDeveloperDocs.html#Resource_Ids">Developer Documentation</A> for more
|
||||
details on the property ID format and naming conventions.</P>
|
||||
|
||||
</BlOCKQUOTE>
|
||||
<H2>Theme Files</H2>
|
||||
|
@ -243,10 +243,10 @@
|
|||
|
||||
<P>Theme Files are used to store saved custom themes. They are simple text files and are
|
||||
stored in the user's home application directory under
|
||||
<code><home>/.ghidra/.ghidra-<version>/themes</code>. The first three properties are always the
|
||||
theme name, the Look and Feel name, and whether the theme uses standard defaults or dark
|
||||
defaults. Finally, there is a list of overridden property "name = value" lines. The format
|
||||
is:</P>
|
||||
<code><home>/.ghidra/.ghidra-<version>/themes</code>. The first three properties
|
||||
are always the theme name, the Look and Feel name, and whether the theme uses standard
|
||||
defaults or dark defaults. Finally, there is a list of overridden property "name = value"
|
||||
lines. The format is:</P>
|
||||
<CODE>
|
||||
<PRE>
|
||||
name = [theme name]
|
||||
|
@ -270,18 +270,18 @@
|
|||
|
||||
color.bg = Black
|
||||
color.bg.foo = #012345
|
||||
[color]Panel.background = Red
|
||||
font.button = dialog-PLAIN-14
|
||||
icon.refresh = images/reload3.png
|
||||
color.bg.bar = color.bg.foo
|
||||
color.bg.xxx = [color]Panel.background
|
||||
[laf.color]Panel.background = silver
|
||||
[laf.color]TextArea.background = [laf.color]Panel.background
|
||||
</PRE>
|
||||
|
||||
|
||||
<P>Each property line is expected to begin with either "color.", "font.", or "icon." Since
|
||||
java defined properties don't start with these prefixes, they will have "[color]", "[font]",
|
||||
or "[icon]" prepended to their property name. These brackets are only used to aid in
|
||||
parsing this file. When the properties are used in Ghidra, the bracketed prefixes are
|
||||
java defined properties don't start with these prefixes, they will have "[laf.color]",
|
||||
"[laf.font]", or "[laf.icon]" prepended to their property name. These brackets are only used
|
||||
to aid in parsing this file. When the properties are used in Ghidra, the bracketed prefixes are
|
||||
removed.</P>
|
||||
|
||||
<P>Also, note that the values of these properties can reference other property names. If the
|
||||
|
|
|
@ -24,7 +24,7 @@ import ghidra.util.task.Task;
|
|||
|
||||
/**
|
||||
* A version of {@link DialogComponentProvider} for clients to extend when they intend for their
|
||||
* dialog to be reused. Typically, dialogs are used once and then no longer referenced.
|
||||
* dialog to be reused. Typically, dialogs are used once and then no longer referenced.
|
||||
* Alternatively, some clients create a dialog and use it for the lifetime of their code. This
|
||||
* is typical of non-modal plugins.
|
||||
* <p>
|
||||
|
@ -32,7 +32,7 @@ import ghidra.util.task.Task;
|
|||
* with the dialog, such as in your plugin's {@code dispose()} method.
|
||||
* <p>
|
||||
* The primary benefit of using this dialog is that any updates to the current theme will update
|
||||
* this dialog, even when the dialog is not visible. For dialogs that extend
|
||||
* this dialog, even when the dialog is not visible. For dialogs that extend
|
||||
* {@link DialogComponentProvider} directly, they only receive theme updates if they are visible.
|
||||
*
|
||||
* @see DialogComponentProvider
|
||||
|
@ -62,12 +62,7 @@ public class ReusableDialogComponentProvider extends DialogComponentProvider {
|
|||
}
|
||||
|
||||
private void themeChanged(ThemeEvent ev) {
|
||||
if (!ev.isLookAndFeelChanged()) {
|
||||
return; // we only care if the Look and Feel changes
|
||||
}
|
||||
|
||||
// if we are visible, then we don't need to update as the system updates all
|
||||
// visible components
|
||||
// if we are visible, then we don't need to update as the system updates all visible components
|
||||
if (isVisible()) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -127,6 +127,7 @@ public class ComponentThemeInspectorAction extends DockingAction {
|
|||
|
||||
Color bg = component.getBackground();
|
||||
Color fg = component.getForeground();
|
||||
Font font = component.getFont();
|
||||
String id;
|
||||
String clazz = component.getClass().getSimpleName();
|
||||
if (clazz.isEmpty()) {
|
||||
|
@ -175,6 +176,10 @@ public class ComponentThemeInspectorAction extends DockingAction {
|
|||
.append(spacer)
|
||||
.append("fg: ")
|
||||
.append(fgText)
|
||||
.append(tabs)
|
||||
.append(spacer)
|
||||
.append("font: ")
|
||||
.append(font)
|
||||
.append('\n');
|
||||
}
|
||||
|
||||
|
|
|
@ -174,6 +174,18 @@ public class ThemeEditorDialog extends DialogComponentProvider {
|
|||
updateButtons();
|
||||
}
|
||||
|
||||
private void resetSelectedLookAndFeel() {
|
||||
Swing.runLater(() -> {
|
||||
try {
|
||||
combo.removeItemListener(comboListener);
|
||||
combo.setSelectedItem(themeManager.getActiveTheme().getLookAndFeelType());
|
||||
}
|
||||
finally {
|
||||
combo.addItemListener(comboListener);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void themeComboChanged(ItemEvent e) {
|
||||
|
||||
if (e.getStateChange() != ItemEvent.SELECTED) {
|
||||
|
@ -181,24 +193,43 @@ public class ThemeEditorDialog extends DialogComponentProvider {
|
|||
}
|
||||
|
||||
LafType lafType = (LafType) e.getItem();
|
||||
Swing.runLater(() -> {
|
||||
if (!themeManager.hasThemeValueChanges()) {
|
||||
setLookAndFeel(lafType);
|
||||
return;
|
||||
}
|
||||
|
||||
themeManager.setLookAndFeel(lafType, lafType.usesDarkDefaults());
|
||||
if (lafType == LafType.GTK) {
|
||||
setStatusText(
|
||||
"Warning - Themes using the GTK LookAndFeel do not support changing java " +
|
||||
"component colors, fonts or icons.",
|
||||
MessageType.ERROR);
|
||||
}
|
||||
else {
|
||||
setStatusText("");
|
||||
}
|
||||
colorTree.rebuild();
|
||||
colorTable.reloadAll();
|
||||
paletteTable.reloadAll();
|
||||
fontTable.reloadAll();
|
||||
iconTable.reloadAll();
|
||||
});
|
||||
//@formatter:off
|
||||
int result = OptionDialog.showOptionDialog(null, "Discard Changes?",
|
||||
"Changing the Look and Feel type will cause you to lose your changes.\n" +
|
||||
"If you would like to keep your changes, cancel this dialog and then save the theme\n" +
|
||||
"Would you like to continue?",
|
||||
"Lose Changes");
|
||||
//@formatter:on
|
||||
if (result == OptionDialog.CANCEL_OPTION) {
|
||||
resetSelectedLookAndFeel();
|
||||
return;
|
||||
}
|
||||
|
||||
setLookAndFeel(lafType);
|
||||
}
|
||||
|
||||
private void setLookAndFeel(LafType lafType) {
|
||||
|
||||
themeManager.setLookAndFeel(lafType, lafType.usesDarkDefaults());
|
||||
if (lafType == LafType.GTK) {
|
||||
setStatusText(
|
||||
"Warning - Themes using the GTK LookAndFeel do not support changing java " +
|
||||
"component colors, fonts or icons.",
|
||||
MessageType.WARNING);
|
||||
}
|
||||
else {
|
||||
setStatusText("");
|
||||
}
|
||||
colorTree.rebuild();
|
||||
colorTable.reloadAll();
|
||||
paletteTable.reloadAll();
|
||||
fontTable.reloadAll();
|
||||
iconTable.reloadAll();
|
||||
}
|
||||
|
||||
private void updateButtons() {
|
||||
|
|
|
@ -4,9 +4,6 @@
|
|||
color.bg = [color]system.color.bg.view
|
||||
color.fg = [color]system.color.fg.view
|
||||
|
||||
// On some LaFs the tables and trees use the bg color we define. Make that consistent for all LaFs.
|
||||
[color]Viewport.background = color.bg
|
||||
|
||||
color.fg.error = color.palette.red
|
||||
color.fg.disabled = color.palette.lightgray
|
||||
color.bg.uneditable = [color]system.color.bg.control
|
||||
|
@ -40,6 +37,27 @@ font.standard = [font]system.font.control
|
|||
font.monospaced = monospaced-PLAIN-12
|
||||
|
||||
|
||||
//
|
||||
// Java LaF Fixups
|
||||
//
|
||||
// Prefer buttons that change on hover
|
||||
[laf.boolean]Button.rollover = true
|
||||
[laf.boolean]Toolbar.isRollover = true
|
||||
|
||||
// Java 1.6 UI consumes MousePressed event when dismissing popup menu
|
||||
// which prevents application components from getting this event.
|
||||
[laf.boolean]PopupMenu.consumeEventOnClose = false
|
||||
|
||||
// On some LaFs the tables and trees use the bg color we define. Make that consistent for all LaFs.
|
||||
[laf.color]Viewport.background = color.bg
|
||||
|
||||
// Fix up the default fonts that Java 1.5.0 changed to Courier
|
||||
[laf.font]TextArea.font = font.monospaced
|
||||
[laf.font]PasswordField.font = font.monospaced
|
||||
|
||||
|
||||
|
||||
|
||||
// Icons files
|
||||
icon.flag = flag.png
|
||||
icon.lock = kgpg.png
|
||||
|
@ -108,3 +126,15 @@ color.cursor.unfocused = color.palette.darkgray
|
|||
icon.make.selection = stack.png
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
[Flat Dark]
|
||||
|
||||
[laf.boolean]ToolBar.focusableButtons = true
|
||||
|
||||
[Flat Light]
|
||||
|
||||
[laf.boolean]ToolBar.focusableButtons = true
|
||||
|
||||
|
||||
|
|
|
@ -94,6 +94,18 @@ public abstract class AbstractThemeReader {
|
|||
reportDuplicateKey(oldValue, lineNumber);
|
||||
}
|
||||
}
|
||||
|
||||
// 'external' look and feel property used by the UIManager
|
||||
else if (BooleanPropertyValue.isBooleanKey(key)) {
|
||||
JavaPropertyValue oldValue =
|
||||
valueMap.addProperty(parseBooleanProperty(key, value, lineNumber));
|
||||
reportDuplicateKey(oldValue, lineNumber);
|
||||
}
|
||||
else if (StringPropertyValue.isStringKey(key)) {
|
||||
JavaPropertyValue oldValue =
|
||||
valueMap.addProperty(parseStringProperty(key, value, lineNumber));
|
||||
reportDuplicateKey(oldValue, lineNumber);
|
||||
}
|
||||
else {
|
||||
error(lineNumber, "Can't process property: " + key + " = " + value);
|
||||
}
|
||||
|
@ -143,6 +155,22 @@ public abstract class AbstractThemeReader {
|
|||
return parsedValue;
|
||||
}
|
||||
|
||||
private BooleanPropertyValue parseBooleanProperty(String key, String value, int lineNumber) {
|
||||
BooleanPropertyValue parsedValue = BooleanPropertyValue.parse(key, value);
|
||||
if (parsedValue == null) {
|
||||
error(lineNumber, "Could not parse boolean property value: " + value);
|
||||
}
|
||||
return parsedValue;
|
||||
}
|
||||
|
||||
private StringPropertyValue parseStringProperty(String key, String value, int lineNumber) {
|
||||
StringPropertyValue parsedValue = StringPropertyValue.parse(key, value);
|
||||
if (parsedValue == null) {
|
||||
error(lineNumber, "Could not parse String property value: " + value);
|
||||
}
|
||||
return parsedValue;
|
||||
}
|
||||
|
||||
private List<Section> readSections(LineNumberReader reader) throws IOException {
|
||||
|
||||
List<Section> sections = new ArrayList<>();
|
||||
|
@ -208,7 +236,7 @@ public abstract class AbstractThemeReader {
|
|||
}
|
||||
|
||||
/**
|
||||
* Represents all the value found in a section of the theme properties file. Sections are
|
||||
* Represents all the value found in a section of the theme properties file. Sections are
|
||||
* defined by a line containing just "[section name]"
|
||||
*/
|
||||
protected class Section {
|
||||
|
@ -287,7 +315,7 @@ public abstract class AbstractThemeReader {
|
|||
}
|
||||
|
||||
/**
|
||||
* Adds a raw line from the file to this section. The line will be parsed into a a
|
||||
* Adds a raw line from the file to this section. The line will be parsed into a a
|
||||
* key-value pair.
|
||||
* @param line the line to be added/parsed
|
||||
* @param lineNumber the line number in the file for this line
|
||||
|
|
|
@ -73,7 +73,7 @@ public class ApplicationThemeManager extends ThemeManager {
|
|||
|
||||
@Override
|
||||
public void restoreThemeValues() {
|
||||
applicationDefaults = getApplicationDefaults();
|
||||
applicationDefaults = loadApplicationDefaults();
|
||||
buildCurrentValues();
|
||||
lookAndFeelManager.resetAll(javaDefaults);
|
||||
notifyThemeChanged(new AllValuesChangedThemeEvent(false));
|
||||
|
@ -288,6 +288,11 @@ public class ApplicationThemeManager extends ThemeManager {
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasThemeValueChanges() {
|
||||
return !changedValuesMap.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerFont(Component component, String fontId) {
|
||||
lookAndFeelManager.registerFont(component, fontId);
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package generic.theme;
|
||||
|
||||
import ghidra.util.Msg;
|
||||
|
||||
/**
|
||||
* A Java property value for keys that use boolean values.
|
||||
*/
|
||||
public class BooleanPropertyValue extends JavaPropertyValue {
|
||||
|
||||
private static final String EXTERNAL_LAF_ID_PREFIX = "[laf.boolean]";
|
||||
|
||||
public BooleanPropertyValue(String id, boolean value) {
|
||||
this(id, null, value);
|
||||
}
|
||||
|
||||
public BooleanPropertyValue(String id, String refId, Boolean value) {
|
||||
super(id, refId, value);
|
||||
}
|
||||
|
||||
public static boolean isBooleanKey(String key) {
|
||||
return key.toLowerCase().startsWith(EXTERNAL_LAF_ID_PREFIX);
|
||||
}
|
||||
|
||||
public static BooleanPropertyValue parse(String key, String value) {
|
||||
String id = fromExternalId(key);
|
||||
|
||||
if (isBooleanKey(value)) {
|
||||
String refId = fromExternalId(value);
|
||||
return new BooleanPropertyValue(key, refId, null);
|
||||
}
|
||||
|
||||
boolean b = Boolean.parseBoolean(value);
|
||||
return new BooleanPropertyValue(id, b);
|
||||
}
|
||||
|
||||
private static String fromExternalId(String externalId) {
|
||||
if (!externalId.toLowerCase().startsWith(EXTERNAL_LAF_ID_PREFIX)) {
|
||||
return externalId;
|
||||
}
|
||||
|
||||
// We return the raw property name (e.g., TextArea.background), not the normalized name
|
||||
// (e.g., laf.color.TextArea.background), since the system currently does not provide the
|
||||
// end-user a way to change these values from the UI.
|
||||
return externalId.substring(EXTERNAL_LAF_ID_PREFIX.length());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object getUnresolvedReferenceValue(String primaryId, String unresolvedId) {
|
||||
Msg.warn(this,
|
||||
"Could not resolve indirect property for \"" + unresolvedId +
|
||||
"\" for primary id \"" + primaryId + "\", using last resort default");
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String toExternalId(String internalId) {
|
||||
return EXTERNAL_LAF_ID_PREFIX + internalId;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getSerializedValue() {
|
||||
return Boolean.toString((Boolean) value);
|
||||
}
|
||||
}
|
|
@ -17,6 +17,8 @@ package generic.theme;
|
|||
|
||||
import java.awt.Color;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.WebColors;
|
||||
import utilities.util.reflection.ReflectionUtilities;
|
||||
|
@ -28,6 +30,10 @@ import utilities.util.reflection.ReflectionUtilities;
|
|||
* and if the class's refId is non-null, then the color value will be null.
|
||||
*/
|
||||
public class ColorValue extends ThemeValue<Color> {
|
||||
|
||||
public static final String LAF_ID_PREFIX = "laf.color.";
|
||||
public static final String EXTERNAL_LAF_ID_PREFIX = "[laf.color]";
|
||||
|
||||
private static final String COLOR_ID_PREFIX = "color.";
|
||||
private static final String EXTERNAL_PREFIX = "[color]";
|
||||
|
||||
|
@ -65,13 +71,14 @@ public class ColorValue extends ThemeValue<Color> {
|
|||
return !id.startsWith(COLOR_ID_PREFIX);
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Returns true if the given key string is a valid external key for a color value
|
||||
* @param key the key string to test
|
||||
* @return true if the given key string is a valid external key for a color value
|
||||
*/
|
||||
public static boolean isColorKey(String key) {
|
||||
return key.startsWith(COLOR_ID_PREFIX) || key.startsWith(EXTERNAL_PREFIX);
|
||||
return StringUtils.startsWithAny(key, COLOR_ID_PREFIX, EXTERNAL_PREFIX,
|
||||
EXTERNAL_LAF_ID_PREFIX);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -115,6 +122,12 @@ public class ColorValue extends ThemeValue<Color> {
|
|||
if (internalId.startsWith(COLOR_ID_PREFIX)) {
|
||||
return internalId;
|
||||
}
|
||||
|
||||
if (internalId.startsWith(LAF_ID_PREFIX)) {
|
||||
String baseId = internalId.substring(LAF_ID_PREFIX.length());
|
||||
return EXTERNAL_LAF_ID_PREFIX + baseId;
|
||||
}
|
||||
|
||||
return EXTERNAL_PREFIX + internalId;
|
||||
}
|
||||
|
||||
|
@ -122,6 +135,9 @@ public class ColorValue extends ThemeValue<Color> {
|
|||
if (externalId.startsWith(EXTERNAL_PREFIX)) {
|
||||
return externalId.substring(EXTERNAL_PREFIX.length());
|
||||
}
|
||||
if (externalId.startsWith(EXTERNAL_LAF_ID_PREFIX)) {
|
||||
return LAF_ID_PREFIX + externalId.substring(EXTERNAL_LAF_ID_PREFIX.length());
|
||||
}
|
||||
return externalId;
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,8 @@ package generic.theme;
|
|||
import java.awt.Font;
|
||||
import java.text.ParseException;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import ghidra.util.Msg;
|
||||
|
||||
/**
|
||||
|
@ -27,9 +29,14 @@ import ghidra.util.Msg;
|
|||
* and if the class's refId is non-null, then the font value will be null.
|
||||
*/
|
||||
public class FontValue extends ThemeValue<Font> {
|
||||
|
||||
public static final String LAF_ID_PREFIX = "laf.font.";
|
||||
public static final String EXTERNAL_LAF_ID_PREFIX = "[laf.font]";
|
||||
|
||||
static final String FONT_ID_PREFIX = "font.";
|
||||
public static final Font LAST_RESORT_DEFAULT = new Font("monospaced", Font.PLAIN, 12);
|
||||
private static final String EXTERNAL_PREFIX = "[font]";
|
||||
|
||||
public static final Font LAST_RESORT_DEFAULT = new Font("monospaced", Font.PLAIN, 12);
|
||||
private FontModifier modifier;
|
||||
|
||||
/**
|
||||
|
@ -97,13 +104,14 @@ public class FontValue extends ThemeValue<Font> {
|
|||
return String.format("%s-%s-%s", font.getName(), getStyleString(font), font.getSize());
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Returns true if the given key string is a valid external key for a font value
|
||||
* @param key the key string to test
|
||||
* @return true if the given key string is a valid external key for a font value
|
||||
*/
|
||||
public static boolean isFontKey(String key) {
|
||||
return key.startsWith(FONT_ID_PREFIX) || key.startsWith(EXTERNAL_PREFIX);
|
||||
return StringUtils.startsWithAny(key, FONT_ID_PREFIX, EXTERNAL_PREFIX,
|
||||
EXTERNAL_LAF_ID_PREFIX);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -112,7 +120,7 @@ public class FontValue extends ThemeValue<Font> {
|
|||
* @param key the key to associate the parsed value with
|
||||
* @param value the font value to parse
|
||||
* @return a FontValue with the given key and the parsed value
|
||||
* @throws ParseException if there is an exception parsing
|
||||
* @throws ParseException if there is an exception parsing
|
||||
*/
|
||||
public static FontValue parse(String key, String value) throws ParseException {
|
||||
String id = fromExternalId(key);
|
||||
|
@ -164,6 +172,12 @@ public class FontValue extends ThemeValue<Font> {
|
|||
if (internalId.startsWith(FONT_ID_PREFIX)) {
|
||||
return internalId;
|
||||
}
|
||||
|
||||
if (internalId.startsWith(LAF_ID_PREFIX)) {
|
||||
String baseId = internalId.substring(LAF_ID_PREFIX.length());
|
||||
return EXTERNAL_LAF_ID_PREFIX + baseId;
|
||||
}
|
||||
|
||||
return EXTERNAL_PREFIX + internalId;
|
||||
}
|
||||
|
||||
|
@ -171,6 +185,9 @@ public class FontValue extends ThemeValue<Font> {
|
|||
if (externalId.startsWith(EXTERNAL_PREFIX)) {
|
||||
return externalId.substring(EXTERNAL_PREFIX.length());
|
||||
}
|
||||
if (externalId.startsWith(EXTERNAL_LAF_ID_PREFIX)) {
|
||||
return LAF_ID_PREFIX + externalId.substring(EXTERNAL_LAF_ID_PREFIX.length());
|
||||
}
|
||||
return externalId;
|
||||
}
|
||||
|
||||
|
@ -198,9 +215,7 @@ public class FontValue extends ThemeValue<Font> {
|
|||
}
|
||||
|
||||
private static FontValue getRefFontValue(String id, String value) throws ParseException {
|
||||
if (value.startsWith(EXTERNAL_PREFIX)) {
|
||||
value = value.substring(EXTERNAL_PREFIX.length());
|
||||
}
|
||||
value = fromExternalId(value);
|
||||
int modIndex = value.indexOf("[");
|
||||
if (modIndex < 0) {
|
||||
return new FontValue(id, fromExternalId(value));
|
||||
|
|
|
@ -33,6 +33,7 @@ public class GThemeValueMap {
|
|||
protected Map<String, ColorValue> colorMap = new HashMap<>();
|
||||
protected Map<String, FontValue> fontMap = new HashMap<>();
|
||||
protected Map<String, IconValue> iconMap = new HashMap<>();
|
||||
protected Map<String, JavaPropertyValue> propertyMap = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Constructs a new empty map.
|
||||
|
@ -88,6 +89,19 @@ public class GThemeValueMap {
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given property value to this map. If a property value already exists in the map with
|
||||
* the same id, it will be replaced.
|
||||
* @param value the {@link JavaPropertyValue} to store in the map.
|
||||
* @return the previous value for the icon key or null if no previous value existed.
|
||||
*/
|
||||
public JavaPropertyValue addProperty(JavaPropertyValue value) {
|
||||
if (value != null) {
|
||||
return propertyMap.put(value.getId(), value);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current {@link ColorValue} for the given id or null if none exists.
|
||||
* @param id the id to look up a color for
|
||||
|
@ -116,7 +130,16 @@ public class GThemeValueMap {
|
|||
}
|
||||
|
||||
/**
|
||||
* Loads all the values from the given map into this map, replacing values with the
|
||||
* Returns the current {@link JavaPropertyValue} for the given id or null if none exists.
|
||||
* @param id the id to look up a icon for
|
||||
* @return the current {@link JavaPropertyValue} for the given id or null if none exists.
|
||||
*/
|
||||
public JavaPropertyValue getProperty(String id) {
|
||||
return propertyMap.get(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all the values from the given map into this map, replacing values with the
|
||||
* same ids.
|
||||
* @param valueMap the map whose values are to be loaded into this map
|
||||
*/
|
||||
|
@ -127,6 +150,7 @@ public class GThemeValueMap {
|
|||
valueMap.colorMap.values().forEach(v -> addColor(v));
|
||||
valueMap.fontMap.values().forEach(v -> addFont(v));
|
||||
valueMap.iconMap.values().forEach(v -> addIcon(v));
|
||||
valueMap.propertyMap.values().forEach(v -> addProperty(v));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -153,6 +177,14 @@ public class GThemeValueMap {
|
|||
return new ArrayList<>(iconMap.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of all the {@link JavaPropertyValue}s stored in this map.
|
||||
* @return a list of all the {@link JavaPropertyValue}s stored in this map.
|
||||
*/
|
||||
public List<JavaPropertyValue> getProperties() {
|
||||
return new ArrayList<>(propertyMap.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if a {@link ColorValue} exists in this map for the given id.
|
||||
* @param id the id to check
|
||||
|
@ -181,11 +213,20 @@ public class GThemeValueMap {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of color, font, and icon values stored in this map
|
||||
* @return the total number of color, font, and icon values stored in this map
|
||||
* Returns true if an {@link JavaPropertyValue} exists in this map for the given id.
|
||||
* @param id the id to check
|
||||
* @return true if an {@link JavaPropertyValue} exists in this map for the given id
|
||||
*/
|
||||
public boolean containsProperty(String id) {
|
||||
return propertyMap.containsKey(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of color, font, icon and property values stored in this map
|
||||
* @return the total number of color, font, icon and property values stored in this map
|
||||
*/
|
||||
public Object size() {
|
||||
return colorMap.size() + fontMap.size() + iconMap.size();
|
||||
return colorMap.size() + fontMap.size() + iconMap.size() + propertyMap.size();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -195,14 +236,16 @@ public class GThemeValueMap {
|
|||
colorMap.clear();
|
||||
fontMap.clear();
|
||||
iconMap.clear();
|
||||
propertyMap.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if there are not color, font, or icon values in this map
|
||||
* @return true if there are not color, font, or icon values in this map
|
||||
* Returns true if there are not color, font, icon or property values in this map
|
||||
* @return true if there are not color, font, icon or property values in this map
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return colorMap.isEmpty() && fontMap.isEmpty() && iconMap.isEmpty();
|
||||
return colorMap.isEmpty() && fontMap.isEmpty() && iconMap.isEmpty() &&
|
||||
propertyMap.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -229,10 +272,18 @@ public class GThemeValueMap {
|
|||
iconMap.remove(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* removes any {@link JavaPropertyValue} with the given id from this map.
|
||||
* @param id the id to remove
|
||||
*/
|
||||
public void removeProperty(String id) {
|
||||
propertyMap.remove(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new {@link GThemeValueMap} that is only populated by values that don't exist
|
||||
* in the give map.
|
||||
* @param base the set of values (usually the default set) to compare against to determine
|
||||
* @param base the set of values (usually the default set) to compare against to determine
|
||||
* what values are changed.
|
||||
* @return a new {@link GThemeValueMap} that is only populated by values that don't exist
|
||||
* in the give map
|
||||
|
@ -254,13 +305,18 @@ public class GThemeValueMap {
|
|||
map.addIcon(icon);
|
||||
}
|
||||
}
|
||||
for (JavaPropertyValue property : propertyMap.values()) {
|
||||
if (!property.equals(base.getProperty(property.getId()))) {
|
||||
map.addProperty(property);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the set of icon (.png, .gif) files that are used by IconValues that came from files
|
||||
* versus resources in the classpath. These are the icon files that need to be included
|
||||
* when exporting this set of values to a zip file.
|
||||
* versus resources in the classpath. These are the icon files that need to be included when
|
||||
* exporting this set of values to a zip file.
|
||||
* @return the set of icon (.png, .gif) files that are used by IconValues that came from files
|
||||
* versus resources in the classpath
|
||||
*/
|
||||
|
@ -268,18 +324,24 @@ public class GThemeValueMap {
|
|||
Set<File> files = new HashSet<>();
|
||||
for (IconValue iconValue : iconMap.values()) {
|
||||
Icon icon = iconValue.getRawValue();
|
||||
if (icon instanceof UrlImageIcon urlIcon) {
|
||||
String originalPath = urlIcon.getOriginalPath();
|
||||
if (originalPath.startsWith(ResourceManager.EXTERNAL_ICON_PREFIX)) {
|
||||
URL url = urlIcon.getUrl();
|
||||
String filePath = url.getFile();
|
||||
if (filePath != null) {
|
||||
File iconFile = new File(filePath);
|
||||
if (iconFile.exists()) {
|
||||
files.add(iconFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!(icon instanceof UrlImageIcon urlIcon)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String originalPath = urlIcon.getOriginalPath();
|
||||
if (!originalPath.startsWith(ResourceManager.EXTERNAL_ICON_PREFIX)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
URL url = urlIcon.getUrl();
|
||||
String filePath = url.getFile();
|
||||
if (filePath == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
File iconFile = new File(filePath);
|
||||
if (iconFile.exists()) {
|
||||
files.add(iconFile);
|
||||
}
|
||||
}
|
||||
return files;
|
||||
|
@ -287,7 +349,7 @@ public class GThemeValueMap {
|
|||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(colorMap, fontMap, iconMap);
|
||||
return Objects.hash(colorMap, fontMap, iconMap, propertyMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -302,8 +364,10 @@ public class GThemeValueMap {
|
|||
return false;
|
||||
}
|
||||
GThemeValueMap other = (GThemeValueMap) obj;
|
||||
return Objects.equals(colorMap, other.colorMap) && Objects.equals(fontMap, other.fontMap) &&
|
||||
Objects.equals(iconMap, other.iconMap);
|
||||
return Objects.equals(colorMap, other.colorMap) &&
|
||||
Objects.equals(fontMap, other.fontMap) &&
|
||||
Objects.equals(iconMap, other.iconMap) &&
|
||||
Objects.equals(propertyMap, other.propertyMap);
|
||||
}
|
||||
|
||||
public void checkForUnresolvedReferences() {
|
||||
|
@ -317,6 +381,9 @@ public class GThemeValueMap {
|
|||
for (IconValue iconValue : iconMap.values()) {
|
||||
iconValue.get(this);
|
||||
}
|
||||
for (JavaPropertyValue propertyValue : propertyMap.values()) {
|
||||
propertyValue.get(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -344,10 +411,18 @@ public class GThemeValueMap {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the resolved color, following indirections as need to get the color ultimately
|
||||
* Returns the set of all Java property ids in this map
|
||||
* @return the set of all Java property ids in this map
|
||||
*/
|
||||
public Set<String> getPropertyIds() {
|
||||
return propertyMap.keySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the resolved color, following indirections as needed to get the color ultimately
|
||||
* assigned to the given id.
|
||||
* @param id the id for which to get a color
|
||||
* @return the resolved color, following indirections as need to get the color ultimately
|
||||
* @return the resolved color, following indirections as needed to get the color ultimately
|
||||
* assigned to the given id.
|
||||
*/
|
||||
public Color getResolvedColor(String id) {
|
||||
|
@ -359,10 +434,10 @@ public class GThemeValueMap {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the resolved font, following indirections as need to get the font ultimately
|
||||
* Returns the resolved font, following indirections as needed to get the font ultimately
|
||||
* assigned to the given id.
|
||||
* @param id the id for which to get a font
|
||||
* @return the resolved font, following indirections as need to get the font ultimately
|
||||
* @return the resolved font, following indirections as needed to get the font ultimately
|
||||
* assigned to the given id
|
||||
*/
|
||||
public Font getResolvedFont(String id) {
|
||||
|
@ -374,10 +449,10 @@ public class GThemeValueMap {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the resolved icon, following indirections as need to get the icon ultimately
|
||||
* Returns the resolved icon, following indirections as needed to get the icon ultimately
|
||||
* assigned to the given id.
|
||||
* @param id the id for which to get an icon
|
||||
* @return the resolved icon, following indirections as need to get the icon ultimately
|
||||
* @return the resolved icon, following indirections as needed to get the icon ultimately
|
||||
* assigned to the given id
|
||||
*/
|
||||
public Icon getResolvedIcon(String id) {
|
||||
|
@ -388,4 +463,18 @@ public class GThemeValueMap {
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the resolved property, following indirections as needed to get the property
|
||||
* ultimately assigned to the given id.
|
||||
* @param id the id for which to get an property
|
||||
* @return the resolved property, following indirections as needed to get the property
|
||||
* ultimately assigned to the given id
|
||||
*/
|
||||
public Object getResolvedProperty(String id) {
|
||||
JavaPropertyValue propertyValue = propertyMap.get(id);
|
||||
if (propertyValue != null) {
|
||||
return propertyValue.get(this);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,8 @@ import java.text.ParseException;
|
|||
|
||||
import javax.swing.Icon;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import ghidra.util.Msg;
|
||||
import resources.ResourceManager;
|
||||
import resources.icons.EmptyIcon;
|
||||
|
@ -33,14 +35,14 @@ import resources.icons.UrlImageIcon;
|
|||
public class IconValue extends ThemeValue<Icon> {
|
||||
private static final String EMPTY_ICON_STRING = "EMPTY_ICON";
|
||||
|
||||
public static final String LAF_ID_PREFIX = "laf.icon.";
|
||||
public static final String EXTERNAL_LAF_ID_PREFIX = "[laf.icon]";
|
||||
|
||||
static final String ICON_ID_PREFIX = "icon.";
|
||||
|
||||
public static final Icon LAST_RESORT_DEFAULT = ResourceManager.getDefaultIcon();
|
||||
|
||||
private static final String EXTERNAL_PREFIX = "[icon]";
|
||||
|
||||
public static final Icon LAST_RESORT_DEFAULT = ResourceManager.getDefaultIcon();
|
||||
private static final int STANDARD_EMPTY_ICON_SIZE = 16;
|
||||
|
||||
private IconModifier modifier;
|
||||
|
||||
/**
|
||||
|
@ -94,13 +96,14 @@ public class IconValue extends ThemeValue<Icon> {
|
|||
return icon;
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Returns true if the given key string is a valid external key for an icon value
|
||||
* @param key the key string to test
|
||||
* @return true if the given key string is a valid external key for an icon value
|
||||
*/
|
||||
public static boolean isIconKey(String key) {
|
||||
return key.startsWith(ICON_ID_PREFIX) || key.startsWith(EXTERNAL_PREFIX);
|
||||
return StringUtils.startsWithAny(key, ICON_ID_PREFIX, EXTERNAL_PREFIX,
|
||||
EXTERNAL_LAF_ID_PREFIX);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -172,9 +175,7 @@ public class IconValue extends ThemeValue<Icon> {
|
|||
}
|
||||
|
||||
private static IconValue parseRefIcon(String id, String value) throws ParseException {
|
||||
if (value.startsWith(EXTERNAL_PREFIX)) {
|
||||
value = value.substring(EXTERNAL_PREFIX.length());
|
||||
}
|
||||
value = fromExternalId(value);
|
||||
int modifierIndex = getModifierIndex(value);
|
||||
if (modifierIndex < 0) {
|
||||
return new IconValue(id, value);
|
||||
|
@ -213,6 +214,12 @@ public class IconValue extends ThemeValue<Icon> {
|
|||
if (internalId.startsWith(ICON_ID_PREFIX)) {
|
||||
return internalId;
|
||||
}
|
||||
|
||||
if (internalId.startsWith(LAF_ID_PREFIX)) {
|
||||
String baseId = internalId.substring(LAF_ID_PREFIX.length());
|
||||
return EXTERNAL_LAF_ID_PREFIX + baseId;
|
||||
}
|
||||
|
||||
return EXTERNAL_PREFIX + internalId;
|
||||
}
|
||||
|
||||
|
@ -220,6 +227,9 @@ public class IconValue extends ThemeValue<Icon> {
|
|||
if (externalId.startsWith(EXTERNAL_PREFIX)) {
|
||||
return externalId.substring(EXTERNAL_PREFIX.length());
|
||||
}
|
||||
if (externalId.startsWith(EXTERNAL_LAF_ID_PREFIX)) {
|
||||
return LAF_ID_PREFIX + externalId.substring(EXTERNAL_LAF_ID_PREFIX.length());
|
||||
}
|
||||
return externalId;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 generic.theme;
|
||||
|
||||
/**
|
||||
* A base class that represents a Java UIManager property. This value is used to allow for
|
||||
* overriding Java UI values using the theme properties files.
|
||||
*/
|
||||
public abstract class JavaPropertyValue extends ThemeValue<Object> {
|
||||
|
||||
public JavaPropertyValue(String id, String refId, Object value) {
|
||||
super(id, refId, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isExternal() {
|
||||
// Java properties are always used to define 'external' UIManager values
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSerializationString() {
|
||||
String outputId = toExternalId(id);
|
||||
return outputId + " = " + getSerializedValue();
|
||||
}
|
||||
|
||||
protected abstract String toExternalId(String internalId);
|
||||
|
||||
protected abstract String getSerializedValue();
|
||||
|
||||
@Override
|
||||
protected ThemeValue<Object> getReferredValue(GThemeValueMap values, String refId) {
|
||||
return values.getProperty(refId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void installValue(ThemeManager themeManager) {
|
||||
// We do not currently support changing these values from the UI or API. Assuming that,
|
||||
// then this method is probably not needed for properties
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package generic.theme;
|
||||
|
||||
import ghidra.util.Msg;
|
||||
|
||||
/**
|
||||
* A Java property value for keys that use String values.
|
||||
*/
|
||||
public class StringPropertyValue extends JavaPropertyValue {
|
||||
|
||||
private static final String EXTERNAL_LAF_ID_PREFIX = "[laf.string]";
|
||||
|
||||
public StringPropertyValue(String id, String value) {
|
||||
this(id, null, value);
|
||||
}
|
||||
|
||||
public StringPropertyValue(String id, String refId, String value) {
|
||||
super(id, refId, value);
|
||||
}
|
||||
|
||||
public static boolean isStringKey(String key) {
|
||||
return key.toLowerCase().startsWith(EXTERNAL_LAF_ID_PREFIX);
|
||||
}
|
||||
|
||||
public static StringPropertyValue parse(String key, String value) {
|
||||
String id = fromExternalId(key);
|
||||
|
||||
if (isStringKey(value)) {
|
||||
String refId = fromExternalId(value);
|
||||
return new StringPropertyValue(id, refId, null);
|
||||
}
|
||||
|
||||
return new StringPropertyValue(id, value);
|
||||
}
|
||||
|
||||
private static String fromExternalId(String externalId) {
|
||||
if (!externalId.toLowerCase().startsWith(EXTERNAL_LAF_ID_PREFIX)) {
|
||||
return externalId;
|
||||
}
|
||||
|
||||
// We return the raw property name (e.g., TextArea.background), not the normalized name
|
||||
// (e.g., laf.color.TextArea.background), since the system currently does not provide the
|
||||
// end-user a way to change these values from the UI.
|
||||
return externalId.substring(EXTERNAL_LAF_ID_PREFIX.length());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object getUnresolvedReferenceValue(String primaryId, String unresolvedId) {
|
||||
Msg.warn(this,
|
||||
"Could not resolve indirect property for \"" + unresolvedId +
|
||||
"\" for primary id \"" + primaryId + "\", using last resort default");
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String toExternalId(String internalId) {
|
||||
return EXTERNAL_LAF_ID_PREFIX + internalId;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getSerializedValue() {
|
||||
return String.valueOf(value);
|
||||
}
|
||||
}
|
|
@ -211,7 +211,7 @@ public class StubThemeManager extends ThemeManager {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected ApplicationThemeDefaults getApplicationDefaults() {
|
||||
protected ApplicationThemeDefaults loadApplicationDefaults() {
|
||||
return new ApplicationThemeDefaults() {
|
||||
|
||||
@Override
|
||||
|
|
|
@ -22,7 +22,7 @@ import generic.theme.laf.UiDefaultsMapper;
|
|||
* and Feel (LaF) is being used.
|
||||
* <P>
|
||||
* Various LaFs have different names for common concepts and even define additional concepts not
|
||||
* listed here. The values in this class are those the application used use regardless of the LaF
|
||||
* listed here. The values in this class are those the application uses use regardless of the LaF
|
||||
* being used. When we load a specific LaF, a {@link UiDefaultsMapper} specific to that LaF is used
|
||||
* to map its common LaF ids to these standard system ids. The {@link GThemeDefaults} uses these
|
||||
* system ids to define colors that can be used throughout the application without using these ids
|
||||
|
|
|
@ -70,6 +70,7 @@ public abstract class ThemeManager {
|
|||
protected LafType activeLafType = activeTheme.getLookAndFeelType();
|
||||
protected boolean useDarkDefaults = activeTheme.useDarkDefaults();
|
||||
|
||||
// this use our normalized ids (e.g., 'laf.')
|
||||
protected GThemeValueMap javaDefaults = new GThemeValueMap();
|
||||
protected GThemeValueMap currentValues = new GThemeValueMap();
|
||||
|
||||
|
@ -89,10 +90,11 @@ public abstract class ThemeManager {
|
|||
// default behavior is only install to INSTANCE if first time
|
||||
INSTANCE = this;
|
||||
}
|
||||
applicationDefaults = getApplicationDefaults();
|
||||
|
||||
applicationDefaults = loadApplicationDefaults();
|
||||
}
|
||||
|
||||
protected ApplicationThemeDefaults getApplicationDefaults() {
|
||||
protected ApplicationThemeDefaults loadApplicationDefaults() {
|
||||
return new PropertyFileThemeDefaults();
|
||||
}
|
||||
|
||||
|
@ -100,9 +102,35 @@ public abstract class ThemeManager {
|
|||
Gui.setThemeManager(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called to create the internal set of theme value used by the application. To
|
||||
* do this, we use a layered approach to install values, with the last values added overwriting
|
||||
* any pre-existing values with the same key. The values are added in the following order:
|
||||
* <pre>
|
||||
* java defaults -> light values -> dark values -> look and feel values -> property file values -> theme values
|
||||
* </pre>
|
||||
* <p>
|
||||
* At the point this method is called, this is the state of these various values:
|
||||
* <ul>
|
||||
* <li>The 'javaValues' are normalized in the form of 'laf.font.TextArea'
|
||||
* </li>
|
||||
* <li>The 'applicationDefaults' contains values loaded from the {@code theme.properties}
|
||||
* files:
|
||||
* <pre>
|
||||
* font.listing.base
|
||||
* font.monospaced
|
||||
* [color]Viewport.background = color.bg
|
||||
* [laf.font]TextArea.font = font.monospaced
|
||||
* [laf.boolean]Button.rollover = true
|
||||
* </pre>
|
||||
* </li>
|
||||
* <li>The 'activeTheme' values are those loaded by the current theme, which has any changes
|
||||
* made to the default values
|
||||
* </li>
|
||||
* </ul>
|
||||
*/
|
||||
protected void buildCurrentValues() {
|
||||
GThemeValueMap map = new GThemeValueMap();
|
||||
|
||||
map.load(javaDefaults);
|
||||
map.load(applicationDefaults.getLightValues());
|
||||
if (useDarkDefaults) {
|
||||
|
@ -500,6 +528,16 @@ public abstract class ThemeManager {
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if any theme values have changed. This does not take into account the current
|
||||
* Look and Feel. Use {@link #hasThemeChanges()} to also account for changes to the Look and
|
||||
* Feel.
|
||||
* @return true if any theme values have changed
|
||||
*/
|
||||
public boolean hasThemeValueChanges() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if an color for the given Id has been defined
|
||||
* @param id the id to check for an existing color.
|
||||
|
|
|
@ -28,6 +28,7 @@ import ghidra.util.Msg;
|
|||
* @param <T> the base type this ThemeValue works on (i.e., Colors, Fonts, Icons)
|
||||
*/
|
||||
public abstract class ThemeValue<T> implements Comparable<ThemeValue<T>> {
|
||||
|
||||
protected final String id;
|
||||
protected final T value;
|
||||
protected final String referenceId;
|
||||
|
@ -36,13 +37,19 @@ public abstract class ThemeValue<T> implements Comparable<ThemeValue<T>> {
|
|||
if (id.equals(referenceId)) {
|
||||
throw new IllegalArgumentException("Can't create a themeValue that referencs itself");
|
||||
}
|
||||
|
||||
if (id.startsWith("[")) {
|
||||
throw new IllegalArgumentException(
|
||||
"Theme values must be constructed with normalized, non-external ids");
|
||||
}
|
||||
|
||||
this.id = id;
|
||||
this.referenceId = referenceId;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* True if this value is one that is one that is defined outside of the application, such as a
|
||||
* True if this value is one that is one that is defined outside of the application, such as a
|
||||
* Java Look and Feel key.
|
||||
* @return true if external
|
||||
*/
|
||||
|
@ -84,7 +91,7 @@ public abstract class ThemeValue<T> implements Comparable<ThemeValue<T>> {
|
|||
* reference chains, an error stack trace will be generated and the default T value will
|
||||
* be returned. In rare situations where it is acceptable for the value to not be resolvable,
|
||||
* use the {@link #hasResolvableValue(GThemeValueMap)} method first.
|
||||
* @param values the {@link GThemeValueMap} used to resolve references if this
|
||||
* @param values the {@link GThemeValueMap} used to resolve references if this
|
||||
* instance doesn't have an actual value.
|
||||
* @return the T value for this instance, following references as needed.
|
||||
*/
|
||||
|
@ -116,7 +123,7 @@ public abstract class ThemeValue<T> implements Comparable<ThemeValue<T>> {
|
|||
* Returns true if the ThemeValue can resolve to the concrete T value (color, font, or icon)
|
||||
* from the given set of theme values.
|
||||
* @param values the set of values to use to try and follow reference chains to ultimately
|
||||
* resolve the ThemeValue to a an actual T value
|
||||
* resolve the ThemeValue to a an actual T value
|
||||
* @return true if the ThemeValue can resolve to the concrete T value (color, font, or icon)
|
||||
* from the given set of theme values.
|
||||
*/
|
||||
|
@ -197,7 +204,7 @@ public abstract class ThemeValue<T> implements Comparable<ThemeValue<T>> {
|
|||
/**
|
||||
* Returns the T to be used if the indirect reference couldn't be resolved.
|
||||
* @param primaryId the id we are trying to get a value for
|
||||
* @param unresolvedId the reference id that couldn't be resolved
|
||||
* @param unresolvedId the reference id that couldn't be resolved
|
||||
* @return the default value to be used if the indirect reference couldn't be resolved.
|
||||
*/
|
||||
protected abstract T getUnresolvedReferenceValue(String primaryId, String unresolvedId);
|
||||
|
|
|
@ -64,7 +64,7 @@ public class CustomNimbusLookAndFeel extends NimbusLookAndFeel {
|
|||
}
|
||||
|
||||
protected void installJavaDefaultsIntoThemeManager(UiDefaultsMapper uiDefaultsMapper) {
|
||||
GThemeValueMap javaDefaults = uiDefaultsMapper.getJavaDefaults();
|
||||
GThemeValueMap javaDefaults = uiDefaultsMapper.getNormalizedJavaDefaults();
|
||||
themeManager.setJavaDefaults(javaDefaults);
|
||||
}
|
||||
|
||||
|
|
|
@ -30,13 +30,13 @@ public class FlatDarkUiDefaultsMapper extends FlatUiDefaultsMapper {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void assignSystemColorValues() {
|
||||
super.assignSystemColorValues();
|
||||
protected void pickRepresentativeValueForColorGroups() {
|
||||
super.pickRepresentativeValueForColorGroups();
|
||||
|
||||
// We don't think the FlatDark LaF's view background (Trees, Tables, Lists) is dark
|
||||
// enough, so we are overriding the view group background and foreground colors
|
||||
assignSystemColorDirect(BG_VIEW_ID, new Color(0x1c1d1e));
|
||||
assignSystemColorDirect(FG_VIEW_ID, WebColors.LIGHT_GRAY);
|
||||
setGroupColor(BG_VIEW_ID, new Color(0x1c1d1e));
|
||||
setGroupColor(FG_VIEW_ID, WebColors.LIGHT_GRAY);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
package generic.theme.laf;
|
||||
|
||||
import javax.swing.UIDefaults;
|
||||
import javax.swing.UIManager;
|
||||
|
||||
import generic.theme.ApplicationThemeManager;
|
||||
import generic.theme.LafType;
|
||||
|
@ -28,16 +27,7 @@ public class FlatLookAndFeelManager extends LookAndFeelManager {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void fixupLookAndFeelIssues() {
|
||||
super.fixupLookAndFeelIssues();
|
||||
|
||||
// We have historically managed button focus-ability ourselves. Allow this by default so
|
||||
// features continue to work as expected, such as right-clicking on ToolButtons.
|
||||
UIManager.put("ToolBar.focusableButtons", Boolean.TRUE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected UiDefaultsMapper getUiDefaultsMapper(UIDefaults defaults) {
|
||||
protected UiDefaultsMapper createUiDefaultsMapper(UIDefaults defaults) {
|
||||
if (getLookAndFeelType() == LafType.FLAT_DARK) {
|
||||
return new FlatDarkUiDefaultsMapper(defaults);
|
||||
}
|
||||
|
|
|
@ -24,42 +24,42 @@ public class FlatUiDefaultsMapper extends UiDefaultsMapper {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void registerIgnoredLafIds() {
|
||||
super.registerIgnoredLafIds();
|
||||
ignoredLafIds.add("Actions.Blue");
|
||||
ignoredLafIds.add("Actions.Green");
|
||||
ignoredLafIds.add("Actions.Grey");
|
||||
ignoredLafIds.add("Actions.Greyinline");
|
||||
ignoredLafIds.add("Actions.Red");
|
||||
ignoredLafIds.add("Actions.Yellow");
|
||||
protected void registerIgnoredJavaIds() {
|
||||
super.registerIgnoredJavaIds();
|
||||
ignoredJavaIds.add("Actions.Blue");
|
||||
ignoredJavaIds.add("Actions.Green");
|
||||
ignoredJavaIds.add("Actions.Grey");
|
||||
ignoredJavaIds.add("Actions.Greyinline");
|
||||
ignoredJavaIds.add("Actions.Red");
|
||||
ignoredJavaIds.add("Actions.Yellow");
|
||||
|
||||
ignoredLafIds.add("Objects.BlackText");
|
||||
ignoredLafIds.add("Objects.Blue");
|
||||
ignoredLafIds.add("Objects.Green");
|
||||
ignoredLafIds.add("Objects.GreenAndroid");
|
||||
ignoredLafIds.add("Objects.Grey");
|
||||
ignoredLafIds.add("Objects.Pink");
|
||||
ignoredLafIds.add("Objects.Purple");
|
||||
ignoredLafIds.add("Objects.Red");
|
||||
ignoredLafIds.add("Objects.RedStatus");
|
||||
ignoredLafIds.add("Objects.Yellow");
|
||||
ignoredLafIds.add("Objects.YellowDark");
|
||||
ignoredJavaIds.add("Objects.BlackText");
|
||||
ignoredJavaIds.add("Objects.Blue");
|
||||
ignoredJavaIds.add("Objects.Green");
|
||||
ignoredJavaIds.add("Objects.GreenAndroid");
|
||||
ignoredJavaIds.add("Objects.Grey");
|
||||
ignoredJavaIds.add("Objects.Pink");
|
||||
ignoredJavaIds.add("Objects.Purple");
|
||||
ignoredJavaIds.add("Objects.Red");
|
||||
ignoredJavaIds.add("Objects.RedStatus");
|
||||
ignoredJavaIds.add("Objects.Yellow");
|
||||
ignoredJavaIds.add("Objects.YellowDark");
|
||||
|
||||
ignoredLafIds.add("h0.font");
|
||||
ignoredLafIds.add("h00.font");
|
||||
ignoredLafIds.add("h1.font");
|
||||
ignoredLafIds.add("h1.regular.font");
|
||||
ignoredLafIds.add("h2.font");
|
||||
ignoredLafIds.add("h2.regular.font");
|
||||
ignoredLafIds.add("h3.font");
|
||||
ignoredLafIds.add("h3.regular.font");
|
||||
ignoredLafIds.add("h4.font");
|
||||
ignoredLafIds.add("large.font");
|
||||
ignoredLafIds.add("light.font");
|
||||
ignoredLafIds.add("medium.font");
|
||||
ignoredLafIds.add("mini.font");
|
||||
ignoredLafIds.add("monospaced.font");
|
||||
ignoredLafIds.add("small.font");
|
||||
ignoredJavaIds.add("h0.font");
|
||||
ignoredJavaIds.add("h00.font");
|
||||
ignoredJavaIds.add("h1.font");
|
||||
ignoredJavaIds.add("h1.regular.font");
|
||||
ignoredJavaIds.add("h2.font");
|
||||
ignoredJavaIds.add("h2.regular.font");
|
||||
ignoredJavaIds.add("h3.font");
|
||||
ignoredJavaIds.add("h3.regular.font");
|
||||
ignoredJavaIds.add("h4.font");
|
||||
ignoredJavaIds.add("large.font");
|
||||
ignoredJavaIds.add("light.font");
|
||||
ignoredJavaIds.add("medium.font");
|
||||
ignoredJavaIds.add("mini.font");
|
||||
ignoredJavaIds.add("monospaced.font");
|
||||
ignoredJavaIds.add("small.font");
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ public class GtkLookAndFeelManager extends LookAndFeelManager {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected UiDefaultsMapper getUiDefaultsMapper(UIDefaults defaults) {
|
||||
protected UiDefaultsMapper createUiDefaultsMapper(UIDefaults defaults) {
|
||||
return new UiDefaultsMapper(defaults);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,7 +70,6 @@ public abstract class LookAndFeelManager {
|
|||
doInstallLookAndFeel();
|
||||
processJavaDefaults();
|
||||
fixupLookAndFeelIssues();
|
||||
installGlobalProperties();
|
||||
installCustomLookAndFeelActions();
|
||||
updateComponentUis();
|
||||
}
|
||||
|
@ -157,22 +156,26 @@ public abstract class LookAndFeelManager {
|
|||
|
||||
/**
|
||||
* Called when one or more fonts have changed.
|
||||
* @param changedJavaFontIds the set of Java Font ids that are affected by this change
|
||||
* <p>
|
||||
* This will update the Java {@link UIManager} and trigger a reload of the UIs.
|
||||
*
|
||||
* @param changedFontIds the set of Java Font ids that are affected by this change; these are
|
||||
* the normalized ids
|
||||
*/
|
||||
public void fontsChanged(Set<String> changedJavaFontIds) {
|
||||
public void fontsChanged(Set<String> changedFontIds) {
|
||||
UIDefaults defaults = UIManager.getDefaults();
|
||||
for (String changedFontId : changedJavaFontIds) {
|
||||
for (String changedFontId : changedFontIds) {
|
||||
// even though all these derive from the new font, they might be different
|
||||
// because of FontModifiers.
|
||||
Font font = Gui.getFont(changedFontId);
|
||||
String lafFontId = normalizedIdToLafIdMap.get(changedFontId);
|
||||
if (lafFontId != null) {
|
||||
String javaFontId = normalizedIdToLafIdMap.get(changedFontId);
|
||||
if (javaFontId != null) {
|
||||
// lafFontId is null for group ids
|
||||
defaults.put(lafFontId, new FontUIResource(font));
|
||||
defaults.put(javaFontId, new FontUIResource(font));
|
||||
}
|
||||
}
|
||||
|
||||
if (!changedJavaFontIds.isEmpty()) {
|
||||
if (!changedFontIds.isEmpty()) {
|
||||
updateComponentUis();
|
||||
}
|
||||
|
||||
|
@ -231,10 +234,23 @@ public abstract class LookAndFeelManager {
|
|||
}
|
||||
|
||||
/**
|
||||
* Subclass may override this method to do specific LookAndFeel fix ups
|
||||
* Subclass may override this method to do specific LookAndFeel fixes.
|
||||
* <p>
|
||||
* This will get called after default values are loaded. This means that any values installed
|
||||
* by this method will overwrite any values registered by the theme.
|
||||
* <p>
|
||||
* Standard properties, such as strings and booleans, can be set inside of the theme
|
||||
* properties files. For more complicated UIManager properties, look and feel classes will
|
||||
* need to override this method and install those directly.
|
||||
* <p>
|
||||
* Any property installed here will not fully be part of the theme system, but rather will be
|
||||
* directly installed into the Java Look and Feel. Thus, properties installed here will be
|
||||
* hard-coded overrides for the system. If we decided that a hard-coded value should be put
|
||||
* into the theme system, then we will need to add support for that property type so that it
|
||||
* can be used when loading the theme files.
|
||||
*/
|
||||
protected void fixupLookAndFeelIssues() {
|
||||
// no generic fix-ups at this time.
|
||||
installGlobalFontSizeOverride();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -244,15 +260,14 @@ public abstract class LookAndFeelManager {
|
|||
*/
|
||||
protected void processJavaDefaults() {
|
||||
UIDefaults defaults = UIManager.getDefaults();
|
||||
UiDefaultsMapper uiDefaultsMapper = getUiDefaultsMapper(defaults);
|
||||
|
||||
GThemeValueMap javaDefaults = uiDefaultsMapper.getJavaDefaults();
|
||||
UiDefaultsMapper uiDefaultsMapper = createUiDefaultsMapper(defaults);
|
||||
GThemeValueMap javaDefaults = uiDefaultsMapper.getNormalizedJavaDefaults();
|
||||
themeManager.setJavaDefaults(javaDefaults);
|
||||
uiDefaultsMapper.installValuesIntoUIDefaults(themeManager.getCurrentValues());
|
||||
normalizedIdToLafIdMap = uiDefaultsMapper.getNormalizedIdToLafIdMap();
|
||||
}
|
||||
|
||||
protected abstract UiDefaultsMapper getUiDefaultsMapper(UIDefaults defaults);
|
||||
protected abstract UiDefaultsMapper createUiDefaultsMapper(UIDefaults defaults);
|
||||
|
||||
protected String findLookAndFeelClassName(String lookAndFeelName) {
|
||||
LookAndFeelInfo[] installedLookAndFeels = UIManager.getInstalledLookAndFeels();
|
||||
|
@ -277,38 +292,20 @@ public abstract class LookAndFeelManager {
|
|||
return false;
|
||||
}
|
||||
|
||||
protected void setKeyBinding(String existingKsText, String newKsText, String[] prefixValues) {
|
||||
protected void setKeyBinding(String existingKsText, String newKsText,
|
||||
String[] prefixValues) {
|
||||
|
||||
KeyStroke existingKs = KeyStroke.getKeyStroke(existingKsText);
|
||||
KeyStroke newKs = KeyStroke.getKeyStroke(newKsText);
|
||||
|
||||
UIDefaults uiDefaults = UIManager.getDefaults();
|
||||
for (String properyPrefix : prefixValues) {
|
||||
|
||||
UIDefaults defaults = UIManager.getDefaults();
|
||||
Object object = defaults.get(properyPrefix + ".focusInputMap");
|
||||
Object object = uiDefaults.get(properyPrefix + ".focusInputMap");
|
||||
InputMap inputMap = (InputMap) object;
|
||||
Object action = inputMap.get(existingKs);
|
||||
inputMap.put(newKs, action);
|
||||
}
|
||||
}
|
||||
|
||||
private void installGlobalLookAndFeelAttributes() {
|
||||
// Fix up the default fonts that Java 1.5.0 changed to Courier, which looked terrible.
|
||||
Font f = new Font("Monospaced", Font.PLAIN, 12);
|
||||
UIManager.put("PasswordField.font", f);
|
||||
UIManager.put("TextArea.font", f);
|
||||
|
||||
// We like buttons that change on hover, so force that to happen (see Tracker SCR 3966)
|
||||
UIManager.put("Button.rollover", Boolean.TRUE);
|
||||
UIManager.put("ToolBar.isRollover", Boolean.TRUE);
|
||||
}
|
||||
|
||||
private void installPopupMenuSettingsOverride() {
|
||||
// Java 1.6 UI consumes MousePressed event when dismissing popup menu
|
||||
// which prevents application components from getting this event.
|
||||
UIManager.put("PopupMenu.consumeEventOnClose", Boolean.FALSE);
|
||||
}
|
||||
|
||||
private void installGlobalFontSizeOverride() {
|
||||
|
||||
// only set a global size if the property is set
|
||||
|
@ -375,12 +372,6 @@ public abstract class LookAndFeelManager {
|
|||
}
|
||||
}
|
||||
|
||||
private void installGlobalProperties() {
|
||||
installGlobalLookAndFeelAttributes();
|
||||
installGlobalFontSizeOverride();
|
||||
installPopupMenuSettingsOverride();
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches the given UIDefaults for ids whose value matches the given class
|
||||
* @param defaults the UIDefaults to search
|
||||
|
|
|
@ -34,7 +34,7 @@ public class MacLookAndFeelManager extends LookAndFeelManager {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected UiDefaultsMapper getUiDefaultsMapper(UIDefaults defaults) {
|
||||
protected UiDefaultsMapper createUiDefaultsMapper(UIDefaults defaults) {
|
||||
return new MacUiDefaultsMapper(defaults);
|
||||
}
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ public class MetalLookAndFeelManager extends LookAndFeelManager {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected UiDefaultsMapper getUiDefaultsMapper(UIDefaults defaults) {
|
||||
protected UiDefaultsMapper createUiDefaultsMapper(UIDefaults defaults) {
|
||||
return new UiDefaultsMapper(defaults);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,12 +30,15 @@ public class MotifLookAndFeelManager extends LookAndFeelManager {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected UiDefaultsMapper getUiDefaultsMapper(UIDefaults defaults) {
|
||||
protected UiDefaultsMapper createUiDefaultsMapper(UIDefaults defaults) {
|
||||
return new MotifUiDefaultsMapper(defaults);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void fixupLookAndFeelIssues() {
|
||||
|
||||
super.fixupLookAndFeelIssues();
|
||||
|
||||
//
|
||||
// The Motif LaF does not bind copy/paste/cut to Control-C/V/X by default. Rather, they
|
||||
// only use the COPY/PASTE/CUT keys. The other LaFs bind both shortcuts.
|
||||
|
|
|
@ -24,9 +24,9 @@ public class MotifUiDefaultsMapper extends UiDefaultsMapper {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void registerIgnoredLafIds() {
|
||||
super.registerIgnoredLafIds();
|
||||
ignoredLafIds.add("controlLightShadow");
|
||||
protected void registerIgnoredJavaIds() {
|
||||
super.registerIgnoredJavaIds();
|
||||
ignoredJavaIds.add("controlLightShadow");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -103,14 +103,14 @@ public class NimbusLookAndFeelManager extends LookAndFeelManager {
|
|||
// fix scroll bar grabber disappearing. See
|
||||
// https://bugs.openjdk.java.net/browse/JDK-8134828. This fix looks like it should not cause
|
||||
// harm even if the bug is fixed on the jdk side.
|
||||
UIDefaults defaults = UIManager.getDefaults();
|
||||
defaults.put("ScrollBar.minimumThumbSize", new Dimension(30, 30));
|
||||
UIDefaults uiDefaults = UIManager.getDefaults();
|
||||
uiDefaults.put("ScrollBar.minimumThumbSize", new Dimension(30, 30));
|
||||
|
||||
// (see NimbusDefaults for key values that can be changed here)
|
||||
}
|
||||
|
||||
@Override
|
||||
protected UiDefaultsMapper getUiDefaultsMapper(UIDefaults defaults) {
|
||||
protected UiDefaultsMapper createUiDefaultsMapper(UIDefaults defaults) {
|
||||
return new NimbusUiDefaultsMapper(defaults);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,45 +28,45 @@ public class NimbusUiDefaultsMapper extends UiDefaultsMapper {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void registerIgnoredLafIds() {
|
||||
super.registerIgnoredLafIds();
|
||||
ignoredLafIds.add("background");
|
||||
protected void registerIgnoredJavaIds() {
|
||||
super.registerIgnoredJavaIds();
|
||||
ignoredJavaIds.add("background");
|
||||
|
||||
ignoredLafIds.add("controlLHighlight");
|
||||
ignoredJavaIds.add("controlLHighlight");
|
||||
|
||||
ignoredLafIds.add("nimbusAlertYellow");
|
||||
ignoredLafIds.add("nimbusBase");
|
||||
ignoredLafIds.add("nimbusBlueGrey");
|
||||
ignoredLafIds.add("nimbusDisabledText");
|
||||
ignoredLafIds.add("nimbusFocus");
|
||||
ignoredLafIds.add("nimbusGreen");
|
||||
ignoredLafIds.add("nimbusInfoBlue");
|
||||
ignoredLafIds.add("nimbusOrange");
|
||||
ignoredLafIds.add("nimbusRed");
|
||||
ignoredLafIds.add("nimbusSelectedText");
|
||||
ignoredLafIds.add("nimbusSelection");
|
||||
ignoredLafIds.add("nimbusSelectionBackground");
|
||||
ignoredJavaIds.add("nimbusAlertYellow");
|
||||
ignoredJavaIds.add("nimbusBase");
|
||||
ignoredJavaIds.add("nimbusBlueGrey");
|
||||
ignoredJavaIds.add("nimbusDisabledText");
|
||||
ignoredJavaIds.add("nimbusFocus");
|
||||
ignoredJavaIds.add("nimbusGreen");
|
||||
ignoredJavaIds.add("nimbusInfoBlue");
|
||||
ignoredJavaIds.add("nimbusOrange");
|
||||
ignoredJavaIds.add("nimbusRed");
|
||||
ignoredJavaIds.add("nimbusSelectedText");
|
||||
ignoredJavaIds.add("nimbusSelection");
|
||||
ignoredJavaIds.add("nimbusSelectionBackground");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void assignSystemColorValues() {
|
||||
protected void pickRepresentativeValueForColorGroups() {
|
||||
|
||||
// different from base class
|
||||
assignSystemColorFromLafId(BG_CONTROL_ID, "Button.background");
|
||||
assignSystemColorFromLafId(FG_CONTROL_ID, "Button.foreground");
|
||||
assignSystemColorFromLafId(BG_BORDER_ID, "nimbusBorder");
|
||||
assignSystemColorFromLafId(BG_VIEW_ID, "nimbusLightBackground");
|
||||
assignSystemColorFromLafId(FG_VIEW_ID, "controlText");
|
||||
setGroupColorUsingJavaRepresentative(BG_CONTROL_ID, "Button.background");
|
||||
setGroupColorUsingJavaRepresentative(FG_CONTROL_ID, "Button.foreground");
|
||||
setGroupColorUsingJavaRepresentative(BG_BORDER_ID, "nimbusBorder");
|
||||
setGroupColorUsingJavaRepresentative(BG_VIEW_ID, "nimbusLightBackground");
|
||||
setGroupColorUsingJavaRepresentative(FG_VIEW_ID, "controlText");
|
||||
|
||||
// the following are the same as the base class (we can't just call super because
|
||||
// it will report errors for missing lafIds such as "window"
|
||||
|
||||
assignSystemColorFromLafId(BG_VIEW_SELECTED_ID, "textHighlight");
|
||||
assignSystemColorFromLafId(FG_VIEW_SELECTED_ID, "textHighlightText");
|
||||
assignSystemColorFromLafId(FG_DISABLED_ID, "textInactiveText");
|
||||
assignSystemColorFromLafId(BG_TOOLTIP_ID, "info");
|
||||
assignSystemColorFromLafId(FG_TOOLTIP_ID, "infoText");
|
||||
setGroupColorUsingJavaRepresentative(BG_VIEW_SELECTED_ID, "textHighlight");
|
||||
setGroupColorUsingJavaRepresentative(FG_VIEW_SELECTED_ID, "textHighlightText");
|
||||
setGroupColorUsingJavaRepresentative(FG_DISABLED_ID, "textInactiveText");
|
||||
setGroupColorUsingJavaRepresentative(BG_TOOLTIP_ID, "info");
|
||||
setGroupColorUsingJavaRepresentative(FG_TOOLTIP_ID, "infoText");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -83,8 +83,8 @@ public class NimbusUiDefaultsMapper extends UiDefaultsMapper {
|
|||
super.installGColorsIntoUIDefaults();
|
||||
|
||||
// The Nimbus selected text field color is not honored if the value is a ColorUIResource.
|
||||
// We install GColorUIResources by default. Thus, our setting for this particular
|
||||
// attribute was being ignored. We set it here to be a GColor, which causes Nimbus
|
||||
// We install GColorUIResources by default. Thus, our setting for this particular
|
||||
// attribute was being ignored. We set it here to be a GColor, which causes Nimbus
|
||||
// to honor the value. We may need to add more entries here as they are discovered.
|
||||
|
||||
defaults.put("TextField.selectionForeground",
|
||||
|
|
|
@ -71,9 +71,13 @@ import ghidra.util.Msg;
|
|||
*
|
||||
*/
|
||||
public class UiDefaultsMapper {
|
||||
public static final String LAF_COLOR_ID_PREFIX = "laf.color.";
|
||||
public static final String LAF_FONT_ID_PREFIX = "laf.font.";
|
||||
public static final String LAF_ICON_ID_PREFIX = "laf.icon.";
|
||||
|
||||
public static final String LAF_COLOR_ID_PREFIX = ColorValue.LAF_ID_PREFIX;
|
||||
public static final String LAF_FONT_ID_PREFIX = FontValue.LAF_ID_PREFIX;
|
||||
public static final String LAF_ICON_ID_PREFIX = IconValue.LAF_ID_PREFIX;
|
||||
|
||||
/** A prefix for UIManager properties that are not colors, fonts or icons (e.g., boolean) */
|
||||
public static final String LAF_PROPERTY_PREFIX = "laf.property.";
|
||||
private static final String LAF_COLOR_PALETTE_PREFIX = "laf.palette.color.";
|
||||
private static final String LAF_FONT_PALETTE_PREFIX = "laf.palette.font.";
|
||||
|
||||
|
@ -86,22 +90,25 @@ public class UiDefaultsMapper {
|
|||
|
||||
protected UIDefaults defaults;
|
||||
private GThemeValueMap extractedValues;
|
||||
|
||||
/** 'normalized' values have keys that start with 'laf.' */
|
||||
private GThemeValueMap normalizedValues = new GThemeValueMap();
|
||||
|
||||
private Map<String, String> lafIdToNormalizedIdMap = new HashMap<>();
|
||||
protected Set<String> ignoredLafIds = new HashSet<>();
|
||||
/** Maps Look and Feel keys to standardized keys that start with 'laf.' */
|
||||
private Map<String, String> javaIdToNormalizedId = new HashMap<>();
|
||||
protected Set<String> ignoredJavaIds = new HashSet<>();
|
||||
|
||||
private Map<String, ColorMatcher> componentToColorMatcherMap = new HashMap<>();
|
||||
private Map<String, FontMatcher> componentToFontMatcherMap = new HashMap<>();
|
||||
private Map<String, ColorGrouper> componentToColorGrouper = new HashMap<>();
|
||||
private Map<String, FontGrouper> componentToFontGrouper = new HashMap<>();
|
||||
|
||||
// @formatter:off
|
||||
protected ColorMatcher viewColorMatcher = new ColorMatcher(BG_VIEW_ID,
|
||||
protected ColorGrouper viewColorGrouper = new ColorGrouper(BG_VIEW_ID,
|
||||
FG_VIEW_ID,
|
||||
BG_VIEW_SELECTED_ID,
|
||||
FG_VIEW_SELECTED_ID);
|
||||
protected ColorMatcher tooltipColorMatcher = new ColorMatcher(BG_TOOLTIP_ID,
|
||||
protected ColorGrouper tooltipColorGrouper = new ColorGrouper(BG_TOOLTIP_ID,
|
||||
FG_TOOLTIP_ID);
|
||||
protected ColorMatcher defaultColorMatcher = new ColorMatcher(BG_CONTROL_ID,
|
||||
protected ColorGrouper defaultColorMatcher = new ColorGrouper(BG_CONTROL_ID,
|
||||
FG_CONTROL_ID,
|
||||
BG_VIEW_ID,
|
||||
FG_VIEW_ID,
|
||||
|
@ -111,9 +118,9 @@ public class UiDefaultsMapper {
|
|||
BG_TOOLTIP_ID,
|
||||
BG_BORDER_ID);
|
||||
|
||||
protected FontMatcher menuFontMatcher = new FontMatcher(FONT_MENU_ID);
|
||||
protected FontMatcher viewFontMatcher = new FontMatcher(FONT_VIEW_ID);
|
||||
protected FontMatcher defaultFontMatcher = new FontMatcher(FONT_CONTROL_ID,
|
||||
protected FontGrouper menuFontGrouper = new FontGrouper(FONT_MENU_ID);
|
||||
protected FontGrouper viewFontGrouper = new FontGrouper(FONT_VIEW_ID);
|
||||
protected FontGrouper defaultFontMatcher = new FontGrouper(FONT_CONTROL_ID,
|
||||
FONT_VIEW_ID,
|
||||
FONT_MENU_ID);
|
||||
|
||||
|
@ -128,13 +135,13 @@ public class UiDefaultsMapper {
|
|||
this.defaults = defaults;
|
||||
this.extractedValues = extractColorFontAndIconValuesFromDefaults();
|
||||
|
||||
assignSystemColorValues();
|
||||
assignSystemFontValues();
|
||||
pickRepresentativeValueForColorGroups();
|
||||
pickRepresentativeValueForFontGroups();
|
||||
|
||||
registerIgnoredLafIds();
|
||||
registerIgnoredJavaIds();
|
||||
|
||||
assignColorMatchersToComponentIds();
|
||||
assignFontMatchersToComponentIds();
|
||||
buildComponentToColorGrouperMap();
|
||||
buildComponentToFontGrouperMap();
|
||||
|
||||
assignNormalizedColorValues();
|
||||
assignNormalizedFontValues();
|
||||
|
@ -142,12 +149,15 @@ public class UiDefaultsMapper {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the normalized id to value map that will be installed into the
|
||||
* ApplicationThemeManager to be the user changeable values for affecting the Java
|
||||
* LookAndFeel colors, fonts, and icons
|
||||
* Returns the normalized id to value map that will be installed into the theme manager to be
|
||||
* the user changeable values for affecting the Java LookAndFeel colors, fonts, and icons.
|
||||
* <p>
|
||||
* The keys in the returned map have been normalized and all start with 'laf.'
|
||||
*
|
||||
*
|
||||
* @return a map of changeable values that affect java LookAndFeel values
|
||||
*/
|
||||
public GThemeValueMap getJavaDefaults() {
|
||||
public GThemeValueMap getNormalizedJavaDefaults() {
|
||||
return normalizedValues;
|
||||
}
|
||||
|
||||
|
@ -161,22 +171,25 @@ public class UiDefaultsMapper {
|
|||
//
|
||||
// In the UI Defaults, colors use indirect values and fonts and icons use direct values.
|
||||
// Here we install our GColors for the indirect colors. Then we set any font and icon
|
||||
// values that are different than the defaults.
|
||||
// values that are different than the defaults. Finally, we apply any overridden Java
|
||||
// properties.
|
||||
//
|
||||
installGColorsIntoUIDefaults();
|
||||
installOverriddenFontsIntoUIDefaults(currentValues);
|
||||
installOverriddenIconsIntoUIDefaults(currentValues);
|
||||
installOverriddenPropertiesIntoUIDefaults(currentValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a mapping of normalized LaF Ids so that when fonts and icons get changed using the
|
||||
* normalized ids that are presented to the user, we know which LaF ids need to be updated in
|
||||
* the UiDefaults so that the LookAndFeel will pick up and use the changes.
|
||||
*
|
||||
* @return a mapping of normalized LaF ids to original LaF ids.
|
||||
*/
|
||||
public Map<String, String> getNormalizedIdToLafIdMap() {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
for (Entry<String, String> entry : lafIdToNormalizedIdMap.entrySet()) {
|
||||
for (Entry<String, String> entry : javaIdToNormalizedId.entrySet()) {
|
||||
String lafId = entry.getKey();
|
||||
String standardId = entry.getValue();
|
||||
map.put(standardId, lafId);
|
||||
|
@ -191,41 +204,41 @@ public class UiDefaultsMapper {
|
|||
* used to seed the values for the system color and fonts. Subclasses should
|
||||
* override this method to add additional ids so they won't show up in the theme values.
|
||||
*/
|
||||
protected void registerIgnoredLafIds() {
|
||||
protected void registerIgnoredJavaIds() {
|
||||
|
||||
ignoredLafIds.add("desktop");
|
||||
ignoredLafIds.add("activeCaption");
|
||||
ignoredLafIds.add("activeCaptionText");
|
||||
ignoredLafIds.add("activeCaptionBorder");
|
||||
ignoredLafIds.add("inactiveCaption");
|
||||
ignoredLafIds.add("inactiveCaptionText");
|
||||
ignoredLafIds.add("inactiveCaptionBorder");
|
||||
ignoredLafIds.add("window");
|
||||
ignoredLafIds.add("windowBorder");
|
||||
ignoredLafIds.add("windowText");
|
||||
ignoredLafIds.add("menu");
|
||||
ignoredLafIds.add("menuText");
|
||||
ignoredLafIds.add("text");
|
||||
ignoredLafIds.add("textText");
|
||||
ignoredLafIds.add("textHighlight");
|
||||
ignoredLafIds.add("textHighightText");
|
||||
ignoredLafIds.add("textInactiveText");
|
||||
ignoredLafIds.add("control");
|
||||
ignoredLafIds.add("controlText");
|
||||
ignoredLafIds.add("controlHighlight");
|
||||
ignoredLafIds.add("controlLtHighlight");
|
||||
ignoredLafIds.add("controlShadow");
|
||||
ignoredLafIds.add("controlDkShadow");
|
||||
ignoredLafIds.add("info");
|
||||
ignoredLafIds.add("infoText");
|
||||
ignoredLafIds.add("scrollbar");
|
||||
ignoredJavaIds.add("desktop");
|
||||
ignoredJavaIds.add("activeCaption");
|
||||
ignoredJavaIds.add("activeCaptionText");
|
||||
ignoredJavaIds.add("activeCaptionBorder");
|
||||
ignoredJavaIds.add("inactiveCaption");
|
||||
ignoredJavaIds.add("inactiveCaptionText");
|
||||
ignoredJavaIds.add("inactiveCaptionBorder");
|
||||
ignoredJavaIds.add("window");
|
||||
ignoredJavaIds.add("windowBorder");
|
||||
ignoredJavaIds.add("windowText");
|
||||
ignoredJavaIds.add("menu");
|
||||
ignoredJavaIds.add("menuText");
|
||||
ignoredJavaIds.add("text");
|
||||
ignoredJavaIds.add("textText");
|
||||
ignoredJavaIds.add("textHighlight");
|
||||
ignoredJavaIds.add("textHighightText");
|
||||
ignoredJavaIds.add("textInactiveText");
|
||||
ignoredJavaIds.add("control");
|
||||
ignoredJavaIds.add("controlText");
|
||||
ignoredJavaIds.add("controlHighlight");
|
||||
ignoredJavaIds.add("controlLtHighlight");
|
||||
ignoredJavaIds.add("controlShadow");
|
||||
ignoredJavaIds.add("controlDkShadow");
|
||||
ignoredJavaIds.add("info");
|
||||
ignoredJavaIds.add("infoText");
|
||||
ignoredJavaIds.add("scrollbar");
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the values to assign to all the system color ids based on the best representative
|
||||
* value defined in the {@link BasicLookAndFeel}
|
||||
*/
|
||||
protected void assignSystemColorValues() {
|
||||
protected void pickRepresentativeValueForColorGroups() {
|
||||
// Originally, these values were assigned to the corresponding concepts as defined
|
||||
// in the BasicLookAndFeel such as "control", "text", etc. Unfortunately, those
|
||||
// conventions are rarely used by specific look and feels. It was discovered that using a
|
||||
|
@ -235,114 +248,118 @@ public class UiDefaultsMapper {
|
|||
// subclassed where the values can be overridden. See the NimbusUiDefaultsMapper as an
|
||||
// example.
|
||||
|
||||
assignSystemColorFromLafId(BG_CONTROL_ID, "Button.background");
|
||||
assignSystemColorFromLafId(FG_CONTROL_ID, "Button.foreground");
|
||||
assignSystemColorFromLafId(BG_BORDER_ID, "InternalFrame.borderColor");
|
||||
setGroupColorUsingJavaRepresentative(BG_CONTROL_ID, "Button.background");
|
||||
setGroupColorUsingJavaRepresentative(FG_CONTROL_ID, "Button.foreground");
|
||||
setGroupColorUsingJavaRepresentative(BG_BORDER_ID, "InternalFrame.borderColor");
|
||||
|
||||
assignSystemColorFromLafId(BG_VIEW_ID, "TextArea.background");
|
||||
assignSystemColorFromLafId(FG_VIEW_ID, "TextArea.foreground");
|
||||
assignSystemColorFromLafId(BG_VIEW_SELECTED_ID, "TextArea.selectionBackground");
|
||||
assignSystemColorFromLafId(FG_VIEW_SELECTED_ID, "TextArea.selectionForeground");
|
||||
setGroupColorUsingJavaRepresentative(BG_VIEW_ID, "TextArea.background");
|
||||
setGroupColorUsingJavaRepresentative(FG_VIEW_ID, "TextArea.foreground");
|
||||
setGroupColorUsingJavaRepresentative(BG_VIEW_SELECTED_ID, "TextArea.selectionBackground");
|
||||
setGroupColorUsingJavaRepresentative(FG_VIEW_SELECTED_ID, "TextArea.selectionForeground");
|
||||
|
||||
assignSystemColorFromLafId(FG_DISABLED_ID, "Label.disabledForeground");
|
||||
setGroupColorUsingJavaRepresentative(FG_DISABLED_ID, "Label.disabledForeground");
|
||||
|
||||
assignSystemColorFromLafId(BG_TOOLTIP_ID, "ToolTip.background");
|
||||
assignSystemColorFromLafId(FG_TOOLTIP_ID, "ToolTip.foreground");
|
||||
setGroupColorUsingJavaRepresentative(BG_TOOLTIP_ID, "ToolTip.background");
|
||||
setGroupColorUsingJavaRepresentative(FG_TOOLTIP_ID, "ToolTip.foreground");
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns the system color id to a color value from the UiDefaults map.
|
||||
* @param systemColorId the system color id to get a value for
|
||||
* @param lafId the LaF key to use to retrieve a color from the UiDefaults
|
||||
* @param group the system color id to get a value for
|
||||
* @param javaId the LaF key to use to retrieve a color from the UiDefaults
|
||||
*/
|
||||
protected void assignSystemColorFromLafId(String systemColorId, String lafId) {
|
||||
Color lafColor = defaults.getColor(lafId);
|
||||
if (lafColor == null) {
|
||||
Msg.debug(this, "Missing value for system color: \"" + systemColorId +
|
||||
"\". No value for laf id: \"" + lafId + "\".");
|
||||
protected void setGroupColorUsingJavaRepresentative(String group, String javaId) {
|
||||
Color javaColor = defaults.getColor(javaId);
|
||||
if (javaColor == null) {
|
||||
Msg.debug(this, "Missing value for system color: \"" + group +
|
||||
"\". No value for java id: \"" + javaId + "\".");
|
||||
return;
|
||||
}
|
||||
normalizedValues.addColor(new ColorValue(systemColorId, lafColor));
|
||||
normalizedValues.addColor(new ColorValue(group, javaColor));
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns the system color id to a directly specified color and does not use the LaF to populate
|
||||
* the system color.
|
||||
* @param systemColorId the system color id to assign the given color
|
||||
* This allows clients to hard-code a chosen color for a group
|
||||
*
|
||||
* @param group the system color id to assign the given color
|
||||
* @param color the color to be assigned to the system color id
|
||||
*/
|
||||
protected void assignSystemColorDirect(String systemColorId, Color color) {
|
||||
normalizedValues.addColor(new ColorValue(systemColorId, color));
|
||||
protected void setGroupColor(String group, Color color) {
|
||||
normalizedValues.addColor(new ColorValue(group, color));
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns the system font id a directly specified font and does not use the LaF to populate
|
||||
* the system font.
|
||||
* @param systemFontId the system font id to assign the given font
|
||||
* This allows clients to hard-code a chosen font for a group
|
||||
*
|
||||
* @param group the system font id to assign the given font
|
||||
* @param font the font to be assigned to the system font id
|
||||
*/
|
||||
protected void assignSystemFontDirect(String systemFontId, Font font) {
|
||||
normalizedValues.addFont(new FontValue(systemFontId, font));
|
||||
protected void setGroupFont(String group, Font font) {
|
||||
normalizedValues.addFont(new FontValue(group, font));
|
||||
}
|
||||
|
||||
protected void setComponentFont(String componentName, Font font) {
|
||||
normalizedValues.addFont(new FontValue(componentName, font));
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the values to use for the system fonts.
|
||||
* Defines the font values to use for each group based upon a chosen Java representative.
|
||||
*/
|
||||
protected void assignSystemFontValues() {
|
||||
assignSystemFontFromLafId(FONT_CONTROL_ID, "Button.font");
|
||||
assignSystemFontFromLafId(FONT_VIEW_ID, "Table.font");
|
||||
assignSystemFontFromLafId(FONT_MENU_ID, "Menu.font");
|
||||
protected void pickRepresentativeValueForFontGroups() {
|
||||
setGroupFontUsingRepresentative(FONT_CONTROL_ID, "Button.font");
|
||||
setGroupFontUsingRepresentative(FONT_VIEW_ID, "Table.font");
|
||||
setGroupFontUsingRepresentative(FONT_MENU_ID, "Menu.font");
|
||||
}
|
||||
|
||||
private void assignSystemFontFromLafId(String systemFontId, String lafId) {
|
||||
private void setGroupFontUsingRepresentative(String fontGroup, String javaId) {
|
||||
|
||||
Font lafFont = extractedValues.getResolvedFont(lafId);
|
||||
if (lafFont == null) {
|
||||
Msg.debug(this, "Missing value for system font: \"" + systemFontId +
|
||||
"\". No value for laf id: \"" + lafId + "\".");
|
||||
Font representativeFont = extractedValues.getResolvedFont(javaId);
|
||||
if (representativeFont == null) {
|
||||
Msg.debug(this, "Missing value for system font: \"" + fontGroup +
|
||||
"\". No value for java id: \"" + javaId + "\".");
|
||||
return;
|
||||
}
|
||||
normalizedValues.addFont(new FontValue(systemFontId, fromUiResource(lafFont)));
|
||||
normalizedValues.addFont(new FontValue(fontGroup, fromUiResource(representativeFont)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns the appropriate font matcher to each component in the related component group
|
||||
* Sets the font grouper for each component group
|
||||
*/
|
||||
protected void assignFontMatchersToComponentIds() {
|
||||
defineComponentFontMatcher(MENU_COMPONENTS, menuFontMatcher);
|
||||
defineComponentFontMatcher(VIEW_COMPONENTS, viewFontMatcher);
|
||||
protected void buildComponentToFontGrouperMap() {
|
||||
mapComponentsToFontGrouper(menuFontGrouper, MENU_COMPONENTS);
|
||||
mapComponentsToFontGrouper(viewFontGrouper, VIEW_COMPONENTS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns the appropriate color matcher to each component in the related component group
|
||||
* Sets the color grouper for each component group
|
||||
*/
|
||||
protected void assignColorMatchersToComponentIds() {
|
||||
defineComponentColorMatcher(VIEW_COMPONENTS, viewColorMatcher);
|
||||
defineComponentColorMatcher(TOOLTIP_COMPONENTS, tooltipColorMatcher);
|
||||
protected void buildComponentToColorGrouperMap() {
|
||||
mapComponentsToColorGrouper(viewColorGrouper, VIEW_COMPONENTS);
|
||||
mapComponentsToColorGrouper(tooltipColorGrouper, TOOLTIP_COMPONENTS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns every component name in the component group to the given ColorValueMatcher
|
||||
* @param componentGroups a list of component names
|
||||
* @param matcher the ColorMatcher that will provide the precedence of system ids to
|
||||
* @param grouper the ColorMatcher that will provide the precedence of system ids to
|
||||
* search when replacing LaF component specific values
|
||||
* @param componentGroup a list of component names
|
||||
*/
|
||||
private void defineComponentColorMatcher(String[] componentGroups, ColorMatcher matcher) {
|
||||
for (String componentGroup : componentGroups) {
|
||||
componentToColorMatcherMap.put(componentGroup, matcher);
|
||||
private void mapComponentsToColorGrouper(ColorGrouper grouper, String... componentGroup) {
|
||||
for (String name : componentGroup) {
|
||||
componentToColorGrouper.put(name, grouper);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns every component name in a component group to the given FontValueMapper
|
||||
* @param componentGroups a list of component names
|
||||
* @param matcher the FontValueMatcher that will provide the precedence of ststem font ids to
|
||||
* @param grouper the FontValueMatcher that will provide the precedence of system font ids to
|
||||
* search when replacing LaF component specific fonts with a system Font
|
||||
* @param componentGroup a list of component names
|
||||
*/
|
||||
private void defineComponentFontMatcher(String[] componentGroups, FontMatcher matcher) {
|
||||
for (String componentGroup : componentGroups) {
|
||||
componentToFontMatcherMap.put(componentGroup, matcher);
|
||||
private void mapComponentsToFontGrouper(FontGrouper grouper, String... componentGroup) {
|
||||
for (String name : componentGroup) {
|
||||
componentToFontGrouper.put(name, grouper);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -356,12 +373,12 @@ public class UiDefaultsMapper {
|
|||
for (String lafId : list) {
|
||||
// we don't want to create java defaults for laf system ids since changing them would
|
||||
// have no effect
|
||||
if (ignoredLafIds.contains(lafId)) {
|
||||
if (ignoredJavaIds.contains(lafId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String createdId = LAF_FONT_ID_PREFIX + lafId;
|
||||
lafIdToNormalizedIdMap.put(lafId, createdId);
|
||||
javaIdToNormalizedId.put(lafId, createdId);
|
||||
|
||||
Font lafFont = extractedValues.getResolvedFont(lafId);
|
||||
FontValue fontValue = getFontValue(createdId, lafId, lafFont);
|
||||
|
@ -380,7 +397,7 @@ public class UiDefaultsMapper {
|
|||
Icon icon = extractedValues.getResolvedIcon(lafId);
|
||||
if (icon != null) {
|
||||
normalizedValues.addIcon(new IconValue(createdId, icon));
|
||||
lafIdToNormalizedIdMap.put(lafId, createdId);
|
||||
javaIdToNormalizedId.put(lafId, createdId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -393,11 +410,11 @@ public class UiDefaultsMapper {
|
|||
List<String> list = new ArrayList<>(extractedValues.getColorIds());
|
||||
Collections.sort(list);
|
||||
for (String lafId : list) {
|
||||
if (ignoredLafIds.contains(lafId)) {
|
||||
if (ignoredJavaIds.contains(lafId)) {
|
||||
continue;
|
||||
}
|
||||
String createdId = LAF_COLOR_ID_PREFIX + lafId;
|
||||
lafIdToNormalizedIdMap.put(lafId, createdId);
|
||||
javaIdToNormalizedId.put(lafId, createdId);
|
||||
|
||||
Color lafColor = extractedValues.getResolvedColor(lafId);
|
||||
ColorValue colorValue = getColorValue(createdId, lafId, lafColor);
|
||||
|
@ -486,16 +503,16 @@ public class UiDefaultsMapper {
|
|||
*/
|
||||
private String findSystemColorId(String lafId, Color lafColor) {
|
||||
String componentName = getComponentName(lafId);
|
||||
ColorMatcher colorMatcher = componentToColorMatcherMap.get(componentName);
|
||||
ColorGrouper colorMatcher = componentToColorGrouper.get(componentName);
|
||||
// check in widget specific group first
|
||||
if (colorMatcher != null) {
|
||||
String systemId = colorMatcher.getSystemId(lafColor);
|
||||
String systemId = colorMatcher.getGroupId(lafColor);
|
||||
if (systemId != null) {
|
||||
return systemId;
|
||||
}
|
||||
}
|
||||
// not found in widget specific group, check general component groups
|
||||
return defaultColorMatcher.getSystemId(lafColor);
|
||||
return defaultColorMatcher.getGroupId(lafColor);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -508,16 +525,16 @@ public class UiDefaultsMapper {
|
|||
*/
|
||||
private String findSystemFontId(String lafId, Font lafFont) {
|
||||
String componentName = getComponentName(lafId);
|
||||
FontMatcher fontMatcher = componentToFontMatcherMap.get(componentName);
|
||||
FontGrouper fontMatcher = componentToFontGrouper.get(componentName);
|
||||
// check in widget specific group first
|
||||
if (fontMatcher != null) {
|
||||
String systemId = fontMatcher.getSystemId(lafFont);
|
||||
String systemId = fontMatcher.getGroupId(lafFont);
|
||||
if (systemId != null) {
|
||||
return systemId;
|
||||
}
|
||||
}
|
||||
// not found in widget specific group, check general component groups
|
||||
return defaultFontMatcher.getSystemId(lafFont);
|
||||
return defaultFontMatcher.getGroupId(lafFont);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -541,7 +558,7 @@ public class UiDefaultsMapper {
|
|||
Map<String, GColorUIResource> cachedColors = new HashMap<>();
|
||||
|
||||
for (String lafId : extractedValues.getColorIds()) {
|
||||
String standardColorId = lafIdToNormalizedIdMap.get(lafId);
|
||||
String standardColorId = javaIdToNormalizedId.get(lafId);
|
||||
if (standardColorId != null) {
|
||||
GColorUIResource sharedGColor = getSharedGColor(cachedColors, standardColorId);
|
||||
defaults.put(lafId, sharedGColor);
|
||||
|
@ -556,7 +573,7 @@ public class UiDefaultsMapper {
|
|||
private void installOverriddenIconsIntoUIDefaults(GThemeValueMap currentValues) {
|
||||
for (String lafId : extractedValues.getIconIds()) {
|
||||
Icon currentIcon = extractedValues.getResolvedIcon(lafId);
|
||||
String standardId = lafIdToNormalizedIdMap.get(lafId);
|
||||
String standardId = javaIdToNormalizedId.get(lafId);
|
||||
Icon overriddenIcon = currentValues.getResolvedIcon(standardId);
|
||||
if (overriddenIcon != null && currentIcon != overriddenIcon) {
|
||||
defaults.put(lafId, overriddenIcon);
|
||||
|
@ -572,7 +589,7 @@ public class UiDefaultsMapper {
|
|||
private void installOverriddenFontsIntoUIDefaults(GThemeValueMap currentValues) {
|
||||
for (String lafId : extractedValues.getFontIds()) {
|
||||
Font currentFont = extractedValues.getResolvedFont(lafId);
|
||||
String standardId = lafIdToNormalizedIdMap.get(lafId);
|
||||
String standardId = javaIdToNormalizedId.get(lafId);
|
||||
Font overriddenFont = currentValues.getResolvedFont(standardId);
|
||||
if (overriddenFont != null && overriddenFont != currentFont) {
|
||||
defaults.put(lafId, new FontUIResource(overriddenFont));
|
||||
|
@ -580,6 +597,18 @@ public class UiDefaultsMapper {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates all non- (color/font/icon) UIManager properties. These properties are UIManager
|
||||
* properties that the user has overridden in the {@code theme.properties} files. These
|
||||
* properties may use any type of value that is not a color/font/icon.
|
||||
* @param currentValues the theme values that potentially override a laf font value
|
||||
*/
|
||||
private void installOverriddenPropertiesIntoUIDefaults(GThemeValueMap currentValues) {
|
||||
for (JavaPropertyValue property : currentValues.getProperties()) {
|
||||
defaults.put(property.getId(), property.get(currentValues));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When putting {@link GColorUIResource} values into the UiDefaults, we need to make sure
|
||||
* we use the same instance for the same color. Some LookAndFeels do "==" checks on colors
|
||||
|
@ -598,7 +627,7 @@ public class UiDefaultsMapper {
|
|||
}
|
||||
|
||||
protected void overrideColor(String lafId, String sytemId) {
|
||||
String normalizedId = lafIdToNormalizedIdMap.get(lafId);
|
||||
String normalizedId = javaIdToNormalizedId.get(lafId);
|
||||
if (normalizedId == null) {
|
||||
Msg.debug(this, "Missing value for laf id: \"" + lafId);
|
||||
return;
|
||||
|
@ -644,54 +673,59 @@ public class UiDefaultsMapper {
|
|||
* @return a list of all ids that have the given value type
|
||||
*/
|
||||
private List<String> getLookAndFeelIdsForType(Class<?> clazz) {
|
||||
List<String> colorKeys = new ArrayList<>();
|
||||
List<String> ids = new ArrayList<>();
|
||||
List<Object> keyList = IteratorUtils.toList(defaults.keys().asIterator());
|
||||
for (Object key : keyList) {
|
||||
if (key instanceof String) {
|
||||
Object value = defaults.get(key);
|
||||
if (clazz.isInstance(value)) {
|
||||
colorKeys.add((String) key);
|
||||
ids.add((String) key);
|
||||
}
|
||||
}
|
||||
}
|
||||
return colorKeys;
|
||||
return ids;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to match values (Colors or Fonts) into appropriate system ids. System ids are searched
|
||||
* in the order the system ids are given in the constructor.
|
||||
* Used to match values (Colors or Fonts) into appropriate system groups. System group are
|
||||
* searched in the order the groups are given in the constructor.
|
||||
* <p>
|
||||
* Groups allow us to use the same group id for many components that by default have the same
|
||||
* value (Color or Font). This grouper allows us to specify the precedence to use when
|
||||
* searching for the best group.
|
||||
*
|
||||
* @param <T> The theme value type (Color or Font)
|
||||
*/
|
||||
private abstract class ValueMatcher<T> {
|
||||
private Map<T, String> map = new HashMap<>();
|
||||
private List<String> systemIdList;
|
||||
private abstract class ValueGrouper<T> {
|
||||
private Map<T, String> idsByFont = new HashMap<>();
|
||||
private List<String> groupIds;
|
||||
private boolean initialized;
|
||||
|
||||
ValueMatcher(String... systemIds) {
|
||||
systemIdList = new ArrayList<>(Arrays.asList(systemIds));
|
||||
ValueGrouper(String... ids) {
|
||||
groupIds = new ArrayList<>(Arrays.asList(ids));
|
||||
}
|
||||
|
||||
private void initialize() {
|
||||
initialized = true;
|
||||
|
||||
// process in reverse order so that earlier items in the list can overwrite later
|
||||
// items if they have the same font
|
||||
for (int i = systemIdList.size() - 1; i >= 0; i--) {
|
||||
String systemId = systemIdList.get(i);
|
||||
T value = getValueFromJavaDefaults(systemId);
|
||||
// items if they have the same value
|
||||
for (int i = groupIds.size() - 1; i >= 0; i--) {
|
||||
String groupId = groupIds.get(i);
|
||||
T value = getValueFromJavaDefaults(groupId);
|
||||
if (value != null) {
|
||||
map.put(value, systemId);
|
||||
idsByFont.put(value, groupId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract T getValueFromJavaDefaults(String systemId);
|
||||
|
||||
String getSystemId(T value) {
|
||||
String getGroupId(T value) {
|
||||
if (!initialized) {
|
||||
initialize();
|
||||
}
|
||||
return map.get(value);
|
||||
return idsByFont.get(value);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -700,9 +734,9 @@ public class UiDefaultsMapper {
|
|||
* id that matches a given color. The order that color system ids are added is important and is
|
||||
* the precedence order if more than one system color id has the same color.
|
||||
*/
|
||||
private class ColorMatcher extends ValueMatcher<Color> {
|
||||
private class ColorGrouper extends ValueGrouper<Color> {
|
||||
|
||||
ColorMatcher(String... systemIds) {
|
||||
ColorGrouper(String... systemIds) {
|
||||
super(systemIds);
|
||||
}
|
||||
|
||||
|
@ -718,8 +752,8 @@ public class UiDefaultsMapper {
|
|||
* that matches a given font. The order that system font ids are added is important and is
|
||||
* the precedence order if more than one system id has the same font.
|
||||
*/
|
||||
private class FontMatcher extends ValueMatcher<Font> {
|
||||
FontMatcher(String... systemIds) {
|
||||
private class FontGrouper extends ValueGrouper<Font> {
|
||||
FontGrouper(String... systemIds) {
|
||||
super(systemIds);
|
||||
}
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ public class WindowsClassicLookAndFeelManager extends LookAndFeelManager {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected UiDefaultsMapper getUiDefaultsMapper(UIDefaults defaults) {
|
||||
protected UiDefaultsMapper createUiDefaultsMapper(UIDefaults defaults) {
|
||||
return new UiDefaultsMapper(defaults);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ public class WindowsLookAndFeelManager extends LookAndFeelManager {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected UiDefaultsMapper getUiDefaultsMapper(UIDefaults defaults) {
|
||||
protected UiDefaultsMapper createUiDefaultsMapper(UIDefaults defaults) {
|
||||
return new UiDefaultsMapper(defaults);
|
||||
}
|
||||
|
||||
|
|
|
@ -352,7 +352,7 @@ public class ApplicationThemeManagerTest {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected ApplicationThemeDefaults getApplicationDefaults() {
|
||||
protected ApplicationThemeDefaults loadApplicationDefaults() {
|
||||
return new ApplicationThemeDefaults() {
|
||||
|
||||
@Override
|
||||
|
|
|
@ -22,7 +22,7 @@ import java.awt.Color;
|
|||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import ghidra.util.WebColors;
|
||||
import ghidra.util.*;
|
||||
|
||||
public class ColorValueTest {
|
||||
|
||||
|
@ -30,6 +30,10 @@ public class ColorValueTest {
|
|||
|
||||
@Before
|
||||
public void setup() {
|
||||
|
||||
// disable warning messages when some test values cannot be found
|
||||
Msg.setErrorLogger(new SpyErrorLogger());
|
||||
|
||||
values = new GThemeValueMap();
|
||||
}
|
||||
|
||||
|
@ -150,4 +154,34 @@ public class ColorValueTest {
|
|||
assertEquals("color.parent", value.getReferenceId());
|
||||
assertNull(value.getRawValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJavaColorValueRoundTrip() {
|
||||
|
||||
ColorValue value = ColorValue.parse("[laf.color]TextArea.background", "red");
|
||||
values.addColor(value);
|
||||
|
||||
assertEquals("laf.color.TextArea.background", value.getId());
|
||||
assertEquals(Color.RED, value.get(values));
|
||||
|
||||
assertEquals("[laf.color]TextArea.background = #ff0000 // Red",
|
||||
value.getSerializationString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInheritsFrom_JavaValues() {
|
||||
|
||||
ColorValue parent = ColorValue.parse("[laf.color]TextArea.background", "red");
|
||||
values.addColor(parent);
|
||||
ColorValue value =
|
||||
ColorValue.parse("[laf.color]Button.background", "[laf.color]TextArea.background");
|
||||
values.addColor(value);
|
||||
|
||||
//
|
||||
// Note: ColorValue.parse() works on external ids or normalized ids.
|
||||
//
|
||||
// ColorValue() constructor and inheritsFrom() only work on normalized ids
|
||||
//
|
||||
assertTrue(value.inheritsFrom("laf.color.TextArea.background", values));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,12 +23,19 @@ import java.text.ParseException;
|
|||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.SpyErrorLogger;
|
||||
|
||||
public class FontValueTest {
|
||||
private static Font FONT = new Font("Dialog", Font.PLAIN, 12);
|
||||
private GThemeValueMap values;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
|
||||
// disable warning messages when some test values cannot be found
|
||||
Msg.setErrorLogger(new SpyErrorLogger());
|
||||
|
||||
values = new GThemeValueMap();
|
||||
}
|
||||
|
||||
|
@ -140,4 +147,33 @@ public class FontValueTest {
|
|||
assertFalse(grandParent.inheritsFrom("font.test", values));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJavaFontValueRoundTrip() throws Exception {
|
||||
|
||||
FontValue value = FontValue.parse("[laf.font]Button.font", "monospaced-PLAIN-12");
|
||||
values.addFont(value);
|
||||
|
||||
assertEquals("laf.font.Button.font", value.getId());
|
||||
assertEquals(new Font("monospaced", Font.PLAIN, 12), value.get(values));
|
||||
|
||||
assertEquals("[laf.font]Button.font = monospaced-PLAIN-12",
|
||||
value.getSerializationString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInheritsFrom_JavaValues() throws Exception {
|
||||
|
||||
FontValue parent = FontValue.parse("[laf.font]Button.font", "monospaced-PLAIN-12");
|
||||
values.addFont(parent);
|
||||
FontValue value =
|
||||
FontValue.parse("[laf.font]ToggleButton.font", "[laf.font]Button.font");
|
||||
values.addFont(value);
|
||||
|
||||
//
|
||||
// Note: ColorValue.parse() works on external ids or normalized ids.
|
||||
//
|
||||
// ColorValue() constructor and inheritsFrom() only work on normalized ids
|
||||
//
|
||||
assertTrue(value.inheritsFrom("laf.font.Button.font", values));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -83,6 +83,8 @@ public class GThemeTest extends AbstractGenericTest {
|
|||
theme.setColor("foo.bar", Color.GREEN);
|
||||
theme.setColorRef("foo.bar.xyz", "foo.bar");
|
||||
|
||||
theme.setColor("laf.color.TextArea.background", Color.GREEN);
|
||||
|
||||
theme.setFont("font.a.1", COURIER);
|
||||
theme.setFont("font.a.2", DIALOG);
|
||||
theme.setFontRef("font.a.3", "font.a.1");
|
||||
|
@ -110,6 +112,7 @@ public class GThemeTest extends AbstractGenericTest {
|
|||
assertEquals(Color.RED, theme.getColor("color.a.4").get(theme));
|
||||
assertEquals(Color.GREEN, theme.getColor("foo.bar").get(theme));
|
||||
assertEquals(Color.GREEN, theme.getColor("foo.bar.xyz").get(theme));
|
||||
assertEquals(Color.GREEN, theme.getColor("laf.color.TextArea.background").get(theme));
|
||||
|
||||
assertEquals(COURIER, theme.getFont("font.a.1").get(theme));
|
||||
assertEquals(DIALOG, theme.getFont("font.a.2").get(theme));
|
||||
|
|
|
@ -24,6 +24,8 @@ import javax.swing.Icon;
|
|||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.SpyErrorLogger;
|
||||
import resources.MultiIcon;
|
||||
import resources.ResourceManager;
|
||||
import resources.icons.EmptyIcon;
|
||||
|
@ -35,6 +37,10 @@ public class IconValueTest {
|
|||
|
||||
@Before
|
||||
public void setup() {
|
||||
|
||||
// disable warning messages when some test values cannot be found
|
||||
Msg.setErrorLogger(new SpyErrorLogger());
|
||||
|
||||
values = new GThemeValueMap();
|
||||
}
|
||||
|
||||
|
@ -225,4 +231,37 @@ public class IconValueTest {
|
|||
assertEquals("icon.test = EMPTY_ICON[size(22,13)]", value.getSerializationString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJavaIconValueRoundTrip() throws Exception {
|
||||
|
||||
IconValue value =
|
||||
IconValue.parse("[laf.icon]FileChooser.homeFolderIcon", "images/go-home.png");
|
||||
values.addIcon(value);
|
||||
|
||||
assertEquals("laf.icon.FileChooser.homeFolderIcon", value.getId());
|
||||
assertEquals(ResourceManager.loadIcon("images/go-home.png"), value.get(values));
|
||||
|
||||
assertEquals("[laf.icon]FileChooser.homeFolderIcon = images/go-home.png",
|
||||
value.getSerializationString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInheritsFrom_JavaValues() throws Exception {
|
||||
|
||||
IconValue parent =
|
||||
IconValue.parse("[laf.icon]FileChooser.homeFolderIcon", "images/go-home.png");
|
||||
values.addIcon(parent);
|
||||
|
||||
IconValue value =
|
||||
IconValue.parse("[laf.icon]FileView.computerIcon",
|
||||
"[laf.icon]FileChooser.homeFolderIcon");
|
||||
values.addIcon(value);
|
||||
|
||||
//
|
||||
// Note: IconValue.parse() works on external ids or normalized ids.
|
||||
//
|
||||
// IconValue() constructor and inheritsFrom() only work on normalized ids
|
||||
//
|
||||
assertTrue(value.inheritsFrom("laf.icon.FileChooser.homeFolderIcon", values));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,8 +36,8 @@ public class ThemePropertyFileReaderTest {
|
|||
@Test
|
||||
public void testDefaults() throws IOException {
|
||||
//@formatter:off
|
||||
ThemePropertyFileReader reader = new ThemePropertyFileReader("test", new StringReader(String.join("\n",
|
||||
"[Defaults]",
|
||||
ThemePropertyFileReader reader = new ThemePropertyFileReader("test", new StringReader(String.join("\n",
|
||||
"[Defaults]",
|
||||
" color.b.1 = white", // WHITE
|
||||
" color.b.2 = #ff0000", // RED
|
||||
" color.b.3 = 0x008000", // GREEN
|
||||
|
@ -53,12 +53,18 @@ public class ThemePropertyFileReaderTest {
|
|||
" icon.a.12 = icon.a.10[size(17,21)]",
|
||||
" icon.a.13 = core.png[size(17,21)]",
|
||||
" icon.a.14 = icon.a.10{core.png[size(4,4)][move(8, 8)]}",
|
||||
" [laf.font]PasswordField.font = font.a.8",
|
||||
" [laf.font]TextArea.font = dialog-PLAIN-14",
|
||||
" [laf.color]TextArea.background = color.b.1",
|
||||
" [laf.string]Fake.title = This is my title",
|
||||
" [laf.string]OtherFake.title = [laf.string]Fake.title",
|
||||
" [laf.boolean]PopupMenu.consumeEventOnClose = false",
|
||||
"")));
|
||||
//@formatter:on
|
||||
|
||||
Color halfAlphaRed = new Color(0x80ff0000, true);
|
||||
GThemeValueMap values = reader.getDefaultValues();
|
||||
assertEquals(15, values.size());
|
||||
assertEquals(21, values.size());
|
||||
|
||||
assertEquals(WHITE, getColor(values, "color.b.1"));
|
||||
assertEquals(RED, getColor(values, "color.b.2"));
|
||||
|
@ -85,21 +91,27 @@ public class ThemePropertyFileReaderTest {
|
|||
icon = getIcon(values, "icon.a.14");
|
||||
assertTrue(icon instanceof MultiIcon);
|
||||
|
||||
Font f = new Font("dialog", Font.PLAIN, 14);
|
||||
assertEquals(f, getFont(values, "laf.font.PasswordField.font")); // direct font
|
||||
assertEquals(f, getFont(values, "laf.font.TextArea.font")); // font reference
|
||||
assertEquals("This is my title", getLafString(values, "Fake.title"));
|
||||
assertEquals("This is my title", getLafString(values, "OtherFake.title"));
|
||||
assertEquals(false, getLafBoolean(values, "PopupMenu.consumeEventOnClose"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDarkDefaults() throws IOException {
|
||||
//@formatter:off
|
||||
ThemePropertyFileReader reader = new ThemePropertyFileReader("test", new StringReader(String.join("\n",
|
||||
"[Defaults]",
|
||||
" color.b.1 = red",
|
||||
" color.b.2 = red",
|
||||
" color.b.3 = red",
|
||||
" color.b.4 = red",
|
||||
" color.b.5 = red",
|
||||
" color.b.6 = red",
|
||||
" color.b.7 = red",
|
||||
"[Dark Defaults]",
|
||||
ThemePropertyFileReader reader = new ThemePropertyFileReader("test", new StringReader(String.join("\n",
|
||||
"[Defaults]",
|
||||
" color.b.1 = red",
|
||||
" color.b.2 = red",
|
||||
" color.b.3 = red",
|
||||
" color.b.4 = red",
|
||||
" color.b.5 = red",
|
||||
" color.b.6 = red",
|
||||
" color.b.7 = red",
|
||||
"[Dark Defaults]",
|
||||
" color.b.1 = white", // WHITE
|
||||
" color.b.2 = #ff0000", // RED
|
||||
" color.b.3 = 0x008000", // GREEN
|
||||
|
@ -126,11 +138,11 @@ public class ThemePropertyFileReaderTest {
|
|||
@Test
|
||||
public void testBothDefaultsAndDarkDefaultsInSameFile() throws IOException {
|
||||
//@formatter:off
|
||||
ThemePropertyFileReader reader = new ThemePropertyFileReader("test", new StringReader(String.join("\n",
|
||||
"[Defaults]",
|
||||
ThemePropertyFileReader reader = new ThemePropertyFileReader("test", new StringReader(String.join("\n",
|
||||
"[Defaults]",
|
||||
" color.b.1 = white", // WHITE
|
||||
" color.b.2 = #ff0000", // RED
|
||||
"[Dark Defaults]",
|
||||
"[Dark Defaults]",
|
||||
" color.b.1 = black", // BLACK
|
||||
" color.b.2 = #0000ff", // BLUE
|
||||
"")));
|
||||
|
@ -151,14 +163,14 @@ public class ThemePropertyFileReaderTest {
|
|||
@Test
|
||||
public void testLookAndFeelValues() throws IOException {
|
||||
//@formatter:off
|
||||
ThemePropertyFileReader reader = new ThemePropertyFileReader("test", new StringReader(String.join("\n",
|
||||
"[Defaults]",
|
||||
" color.b.1 = white",
|
||||
"[Dark Defaults]",
|
||||
" color.b.1 = black",
|
||||
ThemePropertyFileReader reader = new ThemePropertyFileReader("test", new StringReader(String.join("\n",
|
||||
"[Defaults]",
|
||||
" color.b.1 = white",
|
||||
"[Dark Defaults]",
|
||||
" color.b.1 = black",
|
||||
"[Metal]",
|
||||
" color.b.1 = red",
|
||||
"[Nimbus]",
|
||||
"[Nimbus]",
|
||||
" color.b.1 = green",
|
||||
"")));
|
||||
//@formatter:on
|
||||
|
@ -188,8 +200,8 @@ public class ThemePropertyFileReaderTest {
|
|||
@Test
|
||||
public void testParseColorError() throws IOException {
|
||||
//@formatter:off
|
||||
ThemePropertyFileReader reader = new SilentThemePropertyFileReader("test", new StringReader(String.join("\n",
|
||||
"[Defaults]",
|
||||
ThemePropertyFileReader reader = new SilentThemePropertyFileReader("test", new StringReader(String.join("\n",
|
||||
"[Defaults]",
|
||||
" color.b.1 = white", // WHITE
|
||||
" color.b.2 = sdfsdf", // RED
|
||||
"")));
|
||||
|
@ -204,11 +216,11 @@ public class ThemePropertyFileReaderTest {
|
|||
@Test
|
||||
public void testParseFontError() throws IOException {
|
||||
//@formatter:off
|
||||
ThemePropertyFileReader reader = new SilentThemePropertyFileReader("test", new StringReader(String.join("\n",
|
||||
"[Defaults]",
|
||||
" font.b.1 = Dialog-PLAIN-14",
|
||||
" font.b.2 = Dialog-PLANE-13",
|
||||
" font.b.3 = Dialog-BOLD-ITALIC",
|
||||
ThemePropertyFileReader reader = new SilentThemePropertyFileReader("test", new StringReader(String.join("\n",
|
||||
"[Defaults]",
|
||||
" font.b.1 = Dialog-PLAIN-14",
|
||||
" font.b.2 = Dialog-PLANE-13",
|
||||
" font.b.3 = Dialog-BOLD-ITALIC",
|
||||
"")));
|
||||
//@formatter:on
|
||||
List<String> errors = reader.getErrors();
|
||||
|
@ -219,10 +231,10 @@ public class ThemePropertyFileReaderTest {
|
|||
@Test
|
||||
public void testParseFontModiferError() throws IOException {
|
||||
//@formatter:off
|
||||
ThemePropertyFileReader reader = new SilentThemePropertyFileReader("test", new StringReader(String.join("\n",
|
||||
"[Defaults]",
|
||||
" font.b.1 = Dialog-PLAIN-14",
|
||||
" font.b.2 = (font.b.1[)",
|
||||
ThemePropertyFileReader reader = new SilentThemePropertyFileReader("test", new StringReader(String.join("\n",
|
||||
"[Defaults]",
|
||||
" font.b.1 = Dialog-PLAIN-14",
|
||||
" font.b.2 = (font.b.1[)",
|
||||
"")));
|
||||
//@formatter:on
|
||||
List<String> errors = reader.getErrors();
|
||||
|
@ -233,10 +245,10 @@ public class ThemePropertyFileReaderTest {
|
|||
@Test
|
||||
public void testIconNoRightHandValueError() throws IOException {
|
||||
//@formatter:off
|
||||
ThemePropertyFileReader reader = new SilentThemePropertyFileReader("test", new StringReader(String.join("\n",
|
||||
"[Defaults]",
|
||||
" icon.b.1 = core.png",
|
||||
" icon.b.2 = ",
|
||||
ThemePropertyFileReader reader = new SilentThemePropertyFileReader("test", new StringReader(String.join("\n",
|
||||
"[Defaults]",
|
||||
" icon.b.1 = core.png",
|
||||
" icon.b.2 = ",
|
||||
"")));
|
||||
//@formatter:on
|
||||
List<String> errors = reader.getErrors();
|
||||
|
@ -247,8 +259,8 @@ public class ThemePropertyFileReaderTest {
|
|||
@Test
|
||||
public void testColorIdDefinedInNonDefaultsSectionOnly() throws IOException {
|
||||
//@formatter:off
|
||||
ThemePropertyFileReader reader = new SilentThemePropertyFileReader("test", new StringReader(String.join("\n",
|
||||
"[Defaults]",
|
||||
ThemePropertyFileReader reader = new SilentThemePropertyFileReader("test", new StringReader(String.join("\n",
|
||||
"[Defaults]",
|
||||
" color.foo = red",
|
||||
"[Dark Defaults]",
|
||||
" color.bar = blue",
|
||||
|
@ -264,7 +276,7 @@ public class ThemePropertyFileReaderTest {
|
|||
@Test
|
||||
public void testFontIdDefinedInNonDefaultsSectionOnly() throws IOException {
|
||||
//@formatter:off
|
||||
ThemePropertyFileReader reader = new SilentThemePropertyFileReader("test", new StringReader(String.join("\n",
|
||||
ThemePropertyFileReader reader = new SilentThemePropertyFileReader("test", new StringReader(String.join("\n",
|
||||
"[Defaults]",
|
||||
"[Dark Defaults]",
|
||||
" font.bar = dialog-PLAIN-14",
|
||||
|
@ -280,7 +292,7 @@ public class ThemePropertyFileReaderTest {
|
|||
@Test
|
||||
public void testIconIdDefinedInNonDefaultsSectionOnly() throws IOException {
|
||||
//@formatter:off
|
||||
ThemePropertyFileReader reader = new SilentThemePropertyFileReader("test", new StringReader(String.join("\n",
|
||||
ThemePropertyFileReader reader = new SilentThemePropertyFileReader("test", new StringReader(String.join("\n",
|
||||
"[Defaults]",
|
||||
"[Dark Defaults]",
|
||||
" icon.bar = core.png",
|
||||
|
@ -296,8 +308,8 @@ public class ThemePropertyFileReaderTest {
|
|||
@Test
|
||||
public void testDefaultSectionMustBeFirst() throws Exception {
|
||||
//@formatter:off
|
||||
ThemePropertyFileReader reader = new SilentThemePropertyFileReader("test", new StringReader(String.join("\n",
|
||||
"[Dark Defaults]",
|
||||
ThemePropertyFileReader reader = new SilentThemePropertyFileReader("test", new StringReader(String.join("\n",
|
||||
"[Dark Defaults]",
|
||||
" color.foo = red",
|
||||
"[Defaults]",
|
||||
" color.bar = blue",
|
||||
|
@ -326,6 +338,16 @@ public class ThemePropertyFileReaderTest {
|
|||
return icon.get(values);
|
||||
}
|
||||
|
||||
private String getLafString(GThemeValueMap values, String id) {
|
||||
StringPropertyValue value = (StringPropertyValue) values.getProperty(id);
|
||||
return (String) value.get(values);
|
||||
}
|
||||
|
||||
private boolean getLafBoolean(GThemeValueMap values, String id) {
|
||||
BooleanPropertyValue value = (BooleanPropertyValue) values.getProperty(id);
|
||||
return (Boolean) value.get(values);
|
||||
}
|
||||
|
||||
private class SilentThemePropertyFileReader extends ThemePropertyFileReader {
|
||||
|
||||
protected SilentThemePropertyFileReader(String source, Reader reader) throws IOException {
|
||||
|
|
|
@ -28,6 +28,8 @@ import org.junit.Before;
|
|||
import org.junit.Test;
|
||||
|
||||
import generic.theme.*;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.SpyErrorLogger;
|
||||
import resources.ResourceManager;
|
||||
|
||||
public class UIDefaultsMapperTest {
|
||||
|
@ -40,6 +42,10 @@ public class UIDefaultsMapperTest {
|
|||
|
||||
@Before
|
||||
public void setup() {
|
||||
|
||||
// disable warning messages when some default UI values cannot be found
|
||||
Msg.setErrorLogger(new SpyErrorLogger());
|
||||
|
||||
defaults = createDefaults();
|
||||
defaults.put("control", Color.RED);
|
||||
defaults.put("Button.background", Color.RED);
|
||||
|
@ -51,7 +57,7 @@ public class UIDefaultsMapperTest {
|
|||
@Test
|
||||
public void testGetJavaDefaults() {
|
||||
mapper = new UiDefaultsMapper(defaults);
|
||||
GThemeValueMap javaDefaults = mapper.getJavaDefaults();
|
||||
GThemeValueMap javaDefaults = mapper.getNormalizedJavaDefaults();
|
||||
|
||||
assertEquals(Color.RED, javaDefaults.getResolvedColor("system.color.bg.control"));
|
||||
assertEquals(Color.RED, javaDefaults.getResolvedColor("laf.color.Button.background"));
|
||||
|
@ -71,7 +77,7 @@ public class UIDefaultsMapperTest {
|
|||
defaults.put("RadioButton.background", Color.BLUE); // Blue not defined in a color group
|
||||
mapper = new UiDefaultsMapper(defaults);
|
||||
|
||||
GThemeValueMap javaDefaults = mapper.getJavaDefaults();
|
||||
GThemeValueMap javaDefaults = mapper.getNormalizedJavaDefaults();
|
||||
|
||||
// expecting two palette groups to be created
|
||||
String greenPalette = findPaletteColor(javaDefaults, Color.GREEN);
|
||||
|
@ -90,7 +96,7 @@ public class UIDefaultsMapperTest {
|
|||
defaults.put("ToggleButton.font", SMALL_FONT); // Green not defined in a color group
|
||||
mapper = new UiDefaultsMapper(defaults);
|
||||
|
||||
GThemeValueMap javaDefaults = mapper.getJavaDefaults();
|
||||
GThemeValueMap javaDefaults = mapper.getNormalizedJavaDefaults();
|
||||
|
||||
assertDirectFont(javaDefaults, "laf.palette.font.01", SMALL_FONT);
|
||||
assertIndirectFont(javaDefaults, "laf.font.ToggleButton.font", "laf.palette.font.01");
|
||||
|
|
|
@ -210,7 +210,7 @@
|
|||
<target name="___chkstk_ms"/>
|
||||
<pcode>
|
||||
<body><![CDATA[
|
||||
RSP = RSP + 0;
|
||||
RSP = RSP + 8;
|
||||
]]></body>
|
||||
</pcode>
|
||||
</callfixup>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue