mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-22 00:22:25 +08:00
Merge remote-tracking branch 'origin/patch'
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
+4
-4
@@ -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;
|
||||
}
|
||||
|
||||
+5
@@ -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",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user