mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-06 03:50:02 +02:00
GP-1981 Added IconModifiers for sizing,translating, disabling, and
creating overlayed icons in the theme files. Also some VT icon externalization
This commit is contained in:
parent
b2d16ab982
commit
dd31ff47a2
80 changed files with 1555 additions and 525 deletions
|
@ -16,6 +16,7 @@
|
|||
package generic.theme;
|
||||
|
||||
import java.io.*;
|
||||
import java.text.ParseException;
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.util.Msg;
|
||||
|
@ -84,7 +85,7 @@ public abstract class AbstractThemeReader {
|
|||
}
|
||||
else if (IconValue.isIconKey(key)) {
|
||||
if (!GTheme.JAVA_ICON.equals(value)) {
|
||||
valueMap.addIcon(parseIconProperty(key, value));
|
||||
valueMap.addIcon(parseIconProperty(key, value, lineNumber));
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -93,8 +94,14 @@ public abstract class AbstractThemeReader {
|
|||
}
|
||||
}
|
||||
|
||||
private IconValue parseIconProperty(String key, String value) {
|
||||
return IconValue.parse(key, value);
|
||||
private IconValue parseIconProperty(String key, String value, int lineNumber) {
|
||||
try {
|
||||
return IconValue.parse(key, value);
|
||||
}
|
||||
catch (ParseException e) {
|
||||
error(lineNumber, "Could not parse Icon value: " + value + "because " + e.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private FontValue parseFontProperty(String key, String value, int lineNumber) {
|
||||
|
|
|
@ -16,9 +16,15 @@
|
|||
package generic.theme;
|
||||
|
||||
import java.awt.Font;
|
||||
import java.util.*;
|
||||
import java.text.ParseException;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Class that can transform one font into another. For example if want a font that is the same
|
||||
* basic font as some other font, but is just a different size,style, or family, you use a
|
||||
* FontModifier
|
||||
*/
|
||||
public class FontModifier {
|
||||
|
||||
private static final Pattern MODIFIER_PATTERN = Pattern.compile("(\\[([a-zA-Z]+|[0-9]+)\\])*");
|
||||
|
@ -26,16 +32,26 @@ public class FontModifier {
|
|||
private Integer style;
|
||||
private Integer size;
|
||||
|
||||
public FontModifier() {
|
||||
private FontModifier() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new FontModifier that can change a given font by one or more font properties.
|
||||
* @param family if non-null, modifies a font to use this family
|
||||
* @param style if non-null, modifies a font to use this style
|
||||
* @param size if non-null, modifies a font to be this size
|
||||
*/
|
||||
public FontModifier(String family, Integer style, Integer size) {
|
||||
this.family = family;
|
||||
this.style = style;
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the family for modifying a font
|
||||
* @param newFamily the font family to use when modifying fonts
|
||||
*/
|
||||
public void addFamilyModifier(String newFamily) {
|
||||
if (family != null) {
|
||||
throw new IllegalStateException("Multiple font family names specified");
|
||||
|
@ -43,6 +59,10 @@ public class FontModifier {
|
|||
this.family = newFamily;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the font size modifier
|
||||
* @param newSize the size to use when modifying fonts
|
||||
*/
|
||||
public void addSizeModfier(int newSize) {
|
||||
if (size != null) {
|
||||
throw new IllegalStateException("Multiple font sizes specified");
|
||||
|
@ -50,6 +70,10 @@ public class FontModifier {
|
|||
this.size = newSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the font stle modifier. This can be called multiple times to bold and italicize.
|
||||
* @param newStyle the style to use for the font.
|
||||
*/
|
||||
public void addStyleModifier(int newStyle) {
|
||||
if (style == null) {
|
||||
style = newStyle;
|
||||
|
@ -61,6 +85,11 @@ public class FontModifier {
|
|||
style = style | newStyle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a modified font for the given font.
|
||||
* @param font the font to be modified
|
||||
* @return a new modified font
|
||||
*/
|
||||
public Font modify(Font font) {
|
||||
if (family == null) {
|
||||
if (style != null && size != null) {
|
||||
|
@ -76,27 +105,10 @@ public class FontModifier {
|
|||
return new Font(family, newStyle, newSize);
|
||||
}
|
||||
|
||||
public static FontModifier parse(String value) {
|
||||
List<String> modifierValues = getModifierPieces(value);
|
||||
if (modifierValues.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
FontModifier modifier = new FontModifier();
|
||||
for (String modifierString : modifierValues) {
|
||||
if (setSize(modifier, modifierString)) {
|
||||
continue;
|
||||
}
|
||||
if (setStyle(modifier, modifierString)) {
|
||||
continue;
|
||||
}
|
||||
modifier.addFamilyModifier(modifierString);
|
||||
}
|
||||
if (modifier.hadModifications()) {
|
||||
return modifier;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string that can be parsed by the {@link #parse(String)} method of this class
|
||||
* @return a string that can be parsed by the {@link #parse(String)} method of this class
|
||||
*/
|
||||
public String getSerializationString() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
if (family != null) {
|
||||
|
@ -125,14 +137,58 @@ public class FontModifier {
|
|||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given string as one or more font modifiers
|
||||
* @param value the string to parse as modifiers
|
||||
* @return a FontModifier as specified by the given string
|
||||
* @throws ParseException if The value can't be parsed
|
||||
*/
|
||||
public static FontModifier parse(String value) throws ParseException {
|
||||
List<String> modifierValues = ThemeValueUtils.parseGroupings(value, '[', ']');
|
||||
if (modifierValues.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
FontModifier modifier = new FontModifier();
|
||||
for (String modifierString : modifierValues) {
|
||||
if (setSize(modifier, modifierString)) {
|
||||
continue;
|
||||
}
|
||||
if (setStyle(modifier, modifierString)) {
|
||||
continue;
|
||||
}
|
||||
setFamily(modifier, modifierString);
|
||||
}
|
||||
if (modifier.hadModifications()) {
|
||||
return modifier;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void setFamily(FontModifier modifier, String modifierString)
|
||||
throws ParseException {
|
||||
try {
|
||||
modifier.addFamilyModifier(modifierString);
|
||||
}
|
||||
catch (IllegalStateException e) {
|
||||
throw new ParseException("Multiple Font Families specfied", 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private boolean hadModifications() {
|
||||
return family != null || size != null || style != null;
|
||||
}
|
||||
|
||||
private static boolean setStyle(FontModifier modifier, String modifierString) {
|
||||
private static boolean setStyle(FontModifier modifier, String modifierString)
|
||||
throws ParseException {
|
||||
int style = FontValue.getStyle(modifierString);
|
||||
if (style >= 0) {
|
||||
modifier.addStyleModifier(style);
|
||||
try {
|
||||
modifier.addStyleModifier(style);
|
||||
}
|
||||
catch (IllegalStateException e) {
|
||||
throw new ParseException("Illegal style combination", 0);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -149,18 +205,4 @@ public class FontModifier {
|
|||
}
|
||||
}
|
||||
|
||||
private static List<String> getModifierPieces(String value) {
|
||||
if (!MODIFIER_PATTERN.matcher(value).matches()) {
|
||||
throw new IllegalArgumentException("Invalid font modifier string");
|
||||
}
|
||||
StringTokenizer tokenizer = new StringTokenizer(value, "[]");
|
||||
List<String> list = new ArrayList<>();
|
||||
while (tokenizer.hasMoreTokens()) {
|
||||
String token = tokenizer.nextToken().trim();
|
||||
if (!token.isBlank()) {
|
||||
list.add(token);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
package generic.theme;
|
||||
|
||||
import java.awt.Font;
|
||||
import java.text.ParseException;
|
||||
|
||||
import ghidra.util.Msg;
|
||||
|
||||
|
@ -75,7 +76,7 @@ public class FontValue extends ThemeValue<Font> {
|
|||
if (referenceId != null) {
|
||||
String refId = toExternalId(referenceId);
|
||||
if (modifier != null) {
|
||||
return "(" + refId + modifier.getSerializationString() + ")";
|
||||
return refId + modifier.getSerializationString();
|
||||
}
|
||||
return refId;
|
||||
}
|
||||
|
@ -106,8 +107,9 @@ 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
|
||||
*/
|
||||
public static FontValue parse(String key, String value) {
|
||||
public static FontValue parse(String key, String value) throws ParseException {
|
||||
String id = fromExternalId(key);
|
||||
|
||||
value = clean(value);
|
||||
|
@ -184,7 +186,7 @@ public class FontValue extends ThemeValue<Font> {
|
|||
return null;
|
||||
}
|
||||
|
||||
private static FontValue getRefFontValue(String id, String value) {
|
||||
private static FontValue getRefFontValue(String id, String value) throws ParseException {
|
||||
if (value.startsWith(EXTERNAL_PREFIX)) {
|
||||
value = value.substring(EXTERNAL_PREFIX.length());
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ import javax.swing.ImageIcon;
|
|||
|
||||
import ghidra.util.datastruct.WeakStore;
|
||||
import resources.ResourceManager;
|
||||
import resources.icons.UrlImageIcon;
|
||||
import resources.icons.*;
|
||||
|
||||
/**
|
||||
* An {@link Icon} whose value is dynamically determined by looking up its id into a global
|
||||
|
@ -89,10 +89,24 @@ public class GIcon implements Icon {
|
|||
* @return the icon or null
|
||||
*/
|
||||
public URL getUrl() {
|
||||
if (delegate instanceof UrlImageIcon) {
|
||||
return ((UrlImageIcon) delegate).getUrl();
|
||||
return getUrl(delegate);
|
||||
}
|
||||
|
||||
private URL getUrl(Icon icon) {
|
||||
if (icon instanceof UrlImageIcon urlIcon) {
|
||||
return urlIcon.getUrl();
|
||||
}
|
||||
else if (icon instanceof TranslateIcon translateIcon) {
|
||||
return getUrl(translateIcon.getBaseIcon());
|
||||
}
|
||||
else if (icon instanceof DerivedImageIcon derivedIcon) {
|
||||
return getUrl(derivedIcon.getSourceIcon());
|
||||
}
|
||||
else if (icon instanceof RotateIcon rotateIcon) {
|
||||
return getUrl(rotateIcon.getSourceIcon());
|
||||
}
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -125,4 +125,12 @@ public class GThemeDefaults {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class Fonts {
|
||||
public static final String STANDARD = "font.standard";
|
||||
public static final String BOLD = "font.bold";
|
||||
public static final String ITALIC = "font.italic";
|
||||
public static final String BOLD_ITALIC = "font.bold.italic";
|
||||
public static final String MONOSPACED = "font.monospaced";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,295 @@
|
|||
/* ###
|
||||
* 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 java.awt.Dimension;
|
||||
import java.awt.Point;
|
||||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.Icon;
|
||||
//font.foo = images/flag.png[size(12,16)][move(3,4)][disable]
|
||||
|
||||
import resources.MultiIcon;
|
||||
import resources.ResourceManager;
|
||||
import resources.icons.RotateIcon;
|
||||
import resources.icons.TranslateIcon;
|
||||
|
||||
/**
|
||||
* Class that can transform one icon into another. Useful for scaling, translating, disabling,
|
||||
* or overlaying an icon.
|
||||
*/
|
||||
|
||||
public class IconModifier {
|
||||
Dimension size;
|
||||
Point translation;
|
||||
boolean disabled;
|
||||
Integer rotation;
|
||||
List<IconValue> overlayIconValues = null;
|
||||
|
||||
/**
|
||||
* Creates an IconModifier that can scale, translate, or disable an icon.
|
||||
* @param size if non-null, scales an icon to this size.
|
||||
* @param translation if non-null, translates an icon by this amount
|
||||
* @param rotation if non-null, the amount in degrees to rotate the icon
|
||||
* @param disabled if true, creates a disabled version of the icon
|
||||
*/
|
||||
public IconModifier(Dimension size, Point translation, Integer rotation, boolean disabled) {
|
||||
this.size = size;
|
||||
this.translation = translation;
|
||||
this.rotation = rotation;
|
||||
this.disabled = disabled;
|
||||
}
|
||||
|
||||
private IconModifier() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets size modifier. Icons that are modified by this IconModifier will be scaled to this size.
|
||||
* @param size the size to scale modified icons.
|
||||
*/
|
||||
public void setSizeModifier(Dimension size) {
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the translation for this modifier. Icons that are modified by this IconModifier will
|
||||
* be translated by the amount of the given point.
|
||||
* @param point the x,y amount to translate an image
|
||||
*/
|
||||
public void setMoveModifier(Point point) {
|
||||
this.translation = point;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the rotation for this modifier. Icons that are modified by this IconModifier will
|
||||
* be rotated by the given amount (in degrees)
|
||||
* @param degrees the rotation amount;
|
||||
*/
|
||||
public void setRotationModifer(int degrees) {
|
||||
this.rotation = degrees;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this modifier to disable an icon
|
||||
*/
|
||||
public void setDisabled() {
|
||||
disabled = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies the given icon by the any of the modifiers set.
|
||||
* @param icon the icon to be modified
|
||||
* @param values the ThemeValueMap needed if the modify action is to overlay other icons. The
|
||||
* values are used to resolve indirect overlay icon references
|
||||
* @return A new Icon that is a modified version of the given icon
|
||||
*/
|
||||
public Icon modify(Icon icon, GThemeValueMap values) {
|
||||
Icon modified = icon;
|
||||
if (size != null) {
|
||||
modified = ResourceManager.getScaledIcon(modified, size.width, size.height);
|
||||
}
|
||||
if (disabled) {
|
||||
modified = ResourceManager.getDisabledIcon(modified);
|
||||
}
|
||||
if (rotation != null) {
|
||||
modified = new RotateIcon(icon, rotation);
|
||||
}
|
||||
if (translation != null) {
|
||||
modified = new TranslateIcon(modified, translation.x, translation.y);
|
||||
}
|
||||
if (overlayIconValues != null) {
|
||||
MultiIcon multiIcon = new MultiIcon(modified);
|
||||
for (IconValue iconValue : overlayIconValues) {
|
||||
multiIcon.addIcon(iconValue.get(values));
|
||||
}
|
||||
modified = multiIcon;
|
||||
}
|
||||
return modified;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string that can be parsed by the {@link #parse(String)} method of this class
|
||||
* @return a string that can be parsed by the {@link #parse(String)} method of this class
|
||||
*/
|
||||
public String getSerializationString() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
if (size != null) {
|
||||
builder.append("[" + "size(" + size.width + "," + size.height + ")]");
|
||||
}
|
||||
if (rotation != null) {
|
||||
builder.append("[rotate(" + rotation + ")]");
|
||||
}
|
||||
if (translation != null) {
|
||||
builder.append("[" + "move(" + translation.x + "," + translation.y + ")]");
|
||||
}
|
||||
if (disabled) {
|
||||
builder.append("[disabled]");
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given string as one or more icon modifiers
|
||||
* @param iconModifierString the string to parse as modifiers
|
||||
* @return an IconModifier as specified by the given string
|
||||
* @throws ParseException if the iconModifierString in not properly formatted icon modifier
|
||||
*/
|
||||
public static IconModifier parse(String iconModifierString) throws ParseException {
|
||||
if (iconModifierString.isBlank()) {
|
||||
return null;
|
||||
}
|
||||
IconModifier modifier = new IconModifier();
|
||||
String baseModifierString = getBaseModifierString(iconModifierString);
|
||||
parseBaseModifiers(modifier, baseModifierString);
|
||||
|
||||
String overlayValuesString = getIconOverlaysString(iconModifierString);
|
||||
parseOverlayModifiers(modifier, overlayValuesString);
|
||||
if (modifier.hadModifications()) {
|
||||
return modifier;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void parseOverlayModifiers(IconModifier modifier, String overlayValuesString)
|
||||
throws ParseException {
|
||||
List<String> overlayModifierStrings =
|
||||
ThemeValueUtils.parseGroupings(overlayValuesString, '{', '}');
|
||||
for (String overlayIconString : overlayModifierStrings) {
|
||||
IconValue overlayIconValue = IconValue.parse("", overlayIconString);
|
||||
modifier.addOverlayIcon(overlayIconValue);
|
||||
}
|
||||
}
|
||||
|
||||
private void addOverlayIcon(IconValue overlayIconValue) {
|
||||
if (overlayIconValues == null) {
|
||||
overlayIconValues = new ArrayList<>();
|
||||
}
|
||||
overlayIconValues.add(overlayIconValue);
|
||||
}
|
||||
|
||||
private static void parseBaseModifiers(IconModifier modifier, String baseModifierString)
|
||||
throws ParseException {
|
||||
List<String> modifierValues = ThemeValueUtils.parseGroupings(baseModifierString, '[', ']');
|
||||
for (String modifierString : modifierValues) {
|
||||
modifierString = modifierString.replaceAll("\\s", "").toLowerCase();
|
||||
|
||||
if (modifierString.startsWith("size")) {
|
||||
parseSizeModifier(modifier, modifierString);
|
||||
}
|
||||
else if (modifierString.startsWith("move")) {
|
||||
parseMoveModifier(modifier, modifierString);
|
||||
}
|
||||
else if (modifierString.startsWith("rotate")) {
|
||||
parseRotateModifier(modifier, modifierString);
|
||||
}
|
||||
else if (modifierString.startsWith("disabled")) {
|
||||
parseDisabledModifier(modifier, modifierString);
|
||||
}
|
||||
else {
|
||||
throw new ParseException("Invalid icon modifier: " + modifierString, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String getBaseModifierString(String value) {
|
||||
int overlayStart = value.indexOf("{");
|
||||
if (overlayStart < 0) {
|
||||
return value;
|
||||
}
|
||||
if (overlayStart == 0) {
|
||||
return "";
|
||||
}
|
||||
return value.substring(0, overlayStart);
|
||||
}
|
||||
|
||||
private static String getIconOverlaysString(String value) {
|
||||
int overlayStart = value.indexOf("{");
|
||||
if (overlayStart >= 0) {
|
||||
return value.substring(overlayStart);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
private boolean hadModifications() {
|
||||
return size != null || translation != null || overlayIconValues != null ||
|
||||
rotation != null || disabled;
|
||||
}
|
||||
|
||||
private static void parseDisabledModifier(IconModifier modifier, String modifierString)
|
||||
throws ParseException {
|
||||
if (!modifierString.equals("disabled")) {
|
||||
throw new ParseException("Illegal Icon modifier: " + modifier, 0);
|
||||
}
|
||||
modifier.setDisabled();
|
||||
}
|
||||
|
||||
private static void parseRotateModifier(IconModifier modifier, String modifierString)
|
||||
throws ParseException {
|
||||
String argsString = modifierString.substring("rotate".length());
|
||||
int rotation = parseIntArg(argsString);
|
||||
modifier.setRotationModifer(rotation);
|
||||
}
|
||||
|
||||
private static void parseMoveModifier(IconModifier modifier, String modifierString)
|
||||
throws ParseException {
|
||||
String argsString = modifierString.substring("move".length());
|
||||
Point argValue = parsePointArgs(argsString);
|
||||
modifier.setMoveModifier(argValue);
|
||||
}
|
||||
|
||||
private static void parseSizeModifier(IconModifier modifier, String modifierString)
|
||||
throws ParseException {
|
||||
String argsString = modifierString.substring("size".length());
|
||||
Point argValue = parsePointArgs(argsString);
|
||||
modifier.setSizeModifier(new Dimension(argValue.x, argValue.y));
|
||||
}
|
||||
|
||||
private static Point parsePointArgs(String argsString) throws ParseException {
|
||||
if (!(argsString.startsWith("(") && argsString.endsWith(")"))) {
|
||||
throw new ParseException("Invalid arguments: " + argsString, 0);
|
||||
}
|
||||
argsString = argsString.substring(1, argsString.length() - 1);
|
||||
String[] split = argsString.split(",");
|
||||
if (split.length != 2) {
|
||||
throw new ParseException("Invalid arguments: " + argsString, 0);
|
||||
}
|
||||
try {
|
||||
int arg1 = Integer.parseInt(split[0]);
|
||||
int arg2 = Integer.parseInt(split[1]);
|
||||
return new Point(arg1, arg2);
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
throw new ParseException("Invalid arguments: " + argsString, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private static int parseIntArg(String argString) throws ParseException {
|
||||
if (!(argString.startsWith("(") && argString.endsWith(")"))) {
|
||||
throw new ParseException("Invalid arguments: " + argString, 0);
|
||||
}
|
||||
argString = argString.substring(1, argString.length() - 1);
|
||||
try {
|
||||
return Integer.parseInt(argString);
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
throw new ParseException("Invalid arguments: " + argString, 0);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -15,10 +15,13 @@
|
|||
*/
|
||||
package generic.theme;
|
||||
|
||||
import java.text.ParseException;
|
||||
|
||||
import javax.swing.Icon;
|
||||
|
||||
import ghidra.util.Msg;
|
||||
import resources.ResourceManager;
|
||||
import resources.icons.EmptyIcon;
|
||||
import resources.icons.UrlImageIcon;
|
||||
|
||||
/**
|
||||
|
@ -28,12 +31,18 @@ import resources.icons.UrlImageIcon;
|
|||
* and if the class's refId is non-null, then the icon value will be null.
|
||||
*/
|
||||
public class IconValue extends ThemeValue<Icon> {
|
||||
private static final String EMPTY_ICON_STRING = "EMPTY_ICON";
|
||||
|
||||
static final String ICON_ID_PREFIX = "icon.";
|
||||
|
||||
public static final Icon LAST_RESORT_DEFAULT = ResourceManager.getDefaultIcon();
|
||||
|
||||
private static final String EXTERNAL_PREFIX = "[icon]";
|
||||
|
||||
private static final int STANDARD_EMPTY_ICON_SIZE = 16;
|
||||
|
||||
private IconModifier modifier;
|
||||
|
||||
/**
|
||||
* Constructor used when the ColorValue will have a direct {@link Icon} value. The refId will
|
||||
* be null. Note: if a {@link GIcon} is passed in as the value, then this will be an indirect
|
||||
|
@ -55,6 +64,25 @@ public class IconValue extends ThemeValue<Icon> {
|
|||
super(id, refId, null);
|
||||
}
|
||||
|
||||
private IconValue(String id, String refId, IconModifier modifier) {
|
||||
super(id, refId, null);
|
||||
this.modifier = modifier;
|
||||
}
|
||||
|
||||
private IconValue(String id, Icon icon, IconModifier modifier) {
|
||||
super(id, null, icon);
|
||||
this.modifier = modifier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Icon get(GThemeValueMap values) {
|
||||
Icon icon = super.get(values);
|
||||
if (modifier != null) {
|
||||
return modifier.modify(icon, values);
|
||||
}
|
||||
return icon;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSerializationString() {
|
||||
String outputId = toExternalId(id);
|
||||
|
@ -76,6 +104,15 @@ public class IconValue extends ThemeValue<Icon> {
|
|||
* @return a String that represents the icon
|
||||
*/
|
||||
public static String iconToString(Icon icon) {
|
||||
if (icon instanceof EmptyIcon) {
|
||||
int iconWidth = icon.getIconWidth();
|
||||
int iconHeight = icon.getIconHeight();
|
||||
if (iconWidth == STANDARD_EMPTY_ICON_SIZE && iconHeight == STANDARD_EMPTY_ICON_SIZE) {
|
||||
return EMPTY_ICON_STRING;
|
||||
}
|
||||
return EMPTY_ICON_STRING + "[size(" + iconWidth + "," + iconHeight + ")]";
|
||||
}
|
||||
|
||||
if (icon instanceof UrlImageIcon urlIcon) {
|
||||
return urlIcon.getOriginalPath();
|
||||
}
|
||||
|
@ -88,14 +125,60 @@ public class IconValue extends ThemeValue<Icon> {
|
|||
* @param key the key to associate the parsed value with
|
||||
* @param value the color value to parse
|
||||
* @return an IconValue with the given key and the parsed value
|
||||
* @throws ParseException
|
||||
*/
|
||||
public static IconValue parse(String key, String value) {
|
||||
public static IconValue parse(String key, String value) throws ParseException {
|
||||
String id = fromExternalId(key);
|
||||
if (isIconKey(value)) {
|
||||
return new IconValue(id, fromExternalId(value));
|
||||
return parseRefIcon(id, value);
|
||||
}
|
||||
Icon icon = ResourceManager.loadImage(value);
|
||||
return new IconValue(id, icon);
|
||||
return parseIcon(id, value);
|
||||
}
|
||||
|
||||
private static IconValue parseIcon(String id, String value) throws ParseException {
|
||||
int modifierIndex = getModifierIndex(value);
|
||||
|
||||
if (modifierIndex < 0) {
|
||||
return new IconValue(id, getIcon(value));
|
||||
}
|
||||
|
||||
String baseIconString = value.substring(0, modifierIndex).trim();
|
||||
Icon icon = getIcon(baseIconString);
|
||||
String iconModifierString = value.substring(modifierIndex);
|
||||
IconModifier modifier = IconModifier.parse(iconModifierString);
|
||||
return new IconValue(id, icon, modifier);
|
||||
}
|
||||
|
||||
private static Icon getIcon(String baseIconString) {
|
||||
if (EMPTY_ICON_STRING.equals(baseIconString)) {
|
||||
return new EmptyIcon(STANDARD_EMPTY_ICON_SIZE, STANDARD_EMPTY_ICON_SIZE);
|
||||
}
|
||||
return ResourceManager.loadImage(baseIconString);
|
||||
}
|
||||
|
||||
private static IconValue parseRefIcon(String id, String value) throws ParseException {
|
||||
if (value.startsWith(EXTERNAL_PREFIX)) {
|
||||
value = value.substring(EXTERNAL_PREFIX.length());
|
||||
}
|
||||
int modifierIndex = getModifierIndex(value);
|
||||
if (modifierIndex < 0) {
|
||||
return new IconValue(id, value);
|
||||
}
|
||||
String refId = value.substring(0, modifierIndex).trim();
|
||||
IconModifier modifier = IconModifier.parse(value.substring(modifierIndex));
|
||||
return new IconValue(id, refId, modifier);
|
||||
}
|
||||
|
||||
private static int getModifierIndex(String value) {
|
||||
int baseModifierIndex = value.indexOf("[", 1); // start past first char as it coud be valid "[EXTERNAL]" prefix
|
||||
int overlayModifierIndex = value.indexOf("{");
|
||||
if (baseModifierIndex < 0) {
|
||||
return overlayModifierIndex;
|
||||
}
|
||||
if (overlayModifierIndex < 0) {
|
||||
return baseModifierIndex;
|
||||
}
|
||||
return Math.min(overlayModifierIndex, baseModifierIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -140,10 +223,17 @@ public class IconValue extends ThemeValue<Icon> {
|
|||
}
|
||||
|
||||
private String getValueOutput() {
|
||||
String outputString = null;
|
||||
if (referenceId != null) {
|
||||
return toExternalId(referenceId);
|
||||
outputString = toExternalId(referenceId);
|
||||
}
|
||||
return iconToString(value);
|
||||
else {
|
||||
outputString = iconToString(value);
|
||||
}
|
||||
if (modifier != null) {
|
||||
outputString += modifier.getSerializationString();
|
||||
}
|
||||
return outputString;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
/* ###
|
||||
* 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 java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ThemeValueUtils {
|
||||
/**
|
||||
* Parses the given source string into a list of strings, one for each group. The startChar
|
||||
* and endChar defined the group characters. So, for example, "(ab (cd))(ef)((gh))" would
|
||||
* result in a list with the following values: "ab (cd)", "ef", and "(gh)"
|
||||
* @param source the source string to parse into groups
|
||||
* @param startChar the character that defines the start of a group
|
||||
* @param endChar the character that defines then end of a group
|
||||
* @return a List of strings, one for each consecutive group contained in the string
|
||||
* @throws ParseException if the groupings are not balanced or missing altogether
|
||||
*/
|
||||
public static List<String> parseGroupings(String source, char startChar, char endChar)
|
||||
throws ParseException {
|
||||
List<String> results = new ArrayList<>();
|
||||
int index = 0;
|
||||
|
||||
while (index < source.length()) {
|
||||
int groupStart = findNextNonWhiteSpaceChar(source, index);
|
||||
if (groupStart < 0) {
|
||||
break;
|
||||
}
|
||||
if (source.charAt(groupStart) != startChar) {
|
||||
throw new ParseException("Error parsing groupings for " + source, index);
|
||||
}
|
||||
int groupEnd = findMatchingEnd(source, groupStart + 1, startChar, endChar);
|
||||
if (groupEnd < 0) {
|
||||
throw new ParseException("Error parsing groupings for " + source, index);
|
||||
}
|
||||
results.add(source.substring(groupStart + 1, groupEnd));
|
||||
index = groupEnd + 1;
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
private static int findMatchingEnd(String source, int index, char startChar, char endChar) {
|
||||
int level = 0;
|
||||
while (index < source.length()) {
|
||||
char c = source.charAt(index);
|
||||
if (c == startChar) {
|
||||
level++;
|
||||
}
|
||||
else if (c == endChar) {
|
||||
if (level == 0) {
|
||||
return index;
|
||||
}
|
||||
level--;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private static int findNextNonWhiteSpaceChar(String source, int index) {
|
||||
while (index < source.length()) {
|
||||
if (!Character.isWhitespace(source.charAt(index))) {
|
||||
return index;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
}
|
|
@ -26,8 +26,6 @@ import javax.swing.ImageIcon;
|
|||
import generic.theme.GIcon;
|
||||
import generic.theme.GThemeDefaults.Colors;
|
||||
import ghidra.util.Msg;
|
||||
import resources.icons.RotateIcon;
|
||||
import resources.icons.TranslateIcon;
|
||||
|
||||
/**
|
||||
* A class to get generic icons for standard actions. All methods in this class return an
|
||||
|
@ -51,7 +49,7 @@ public class Icons {
|
|||
public static final Icon NAVIGATE_ON_INCOMING_EVENT_ICON = new GIcon("icon.navigate.in");
|
||||
public static final Icon NAVIGATE_ON_OUTGOING_EVENT_ICON = new GIcon("icon.navigate.out");
|
||||
|
||||
public static final Icon NOT_ALLOWED_ICON = new GIcon("icon.notallowed");
|
||||
public static final Icon NOT_ALLOWED_ICON = new GIcon("icon.not.allowed");
|
||||
public static final Icon OPEN_FOLDER_ICON = new GIcon("icon.folder.open");
|
||||
public static final Icon REFRESH_ICON = new GIcon("icon.refresh");
|
||||
|
||||
|
@ -78,16 +76,10 @@ public class Icons {
|
|||
// Not necessarily re-usable, but this is needed for the help system; these should
|
||||
// probably be moved to the client that uses them, while updating the
|
||||
// help system to use them there.
|
||||
public static final Icon ARROW_DOWN_RIGHT_ICON =
|
||||
ResourceManager.getImageIcon(new RotateIcon(new GIcon("icon.arrow.up.right"), 90));
|
||||
public static final Icon ARROW_UP_LEFT_ICON =
|
||||
ResourceManager.getImageIcon(new RotateIcon(new GIcon("icon.arrow.up.right"), 275));
|
||||
public static final Icon FILTER_NOT_ACCEPTED_ICON =
|
||||
ResourceManager.getImageIcon(new MultiIcon(new GIcon("icon.flag"),
|
||||
new TranslateIcon(ResourceManager.loadImage("icon.notallowed", 10, 10), 6, 6)));
|
||||
public static final Icon APPLY_BLOCKED_MATCH_ICON =
|
||||
ResourceManager.getImageIcon(new MultiIcon(new GIcon("icon.lock"),
|
||||
new TranslateIcon(ResourceManager.loadImage("icon.checkmark.green", 12, 12), 4, 0)));
|
||||
public static final Icon ARROW_DOWN_RIGHT_ICON = new GIcon("icon.arrow.down.right");
|
||||
public static final Icon ARROW_UP_LEFT_ICON = new GIcon("icon.arrow.up.left");
|
||||
public static final Icon FILTER_NOT_ACCEPTED_ICON = new GIcon("icon.filter.not.accepted");
|
||||
public static final Icon APPLY_BLOCKED_MATCH_ICON = new GIcon("icon.blocked.match");
|
||||
|
||||
/**
|
||||
* Returns true if the given string is a Java code snippet that references this class
|
||||
|
|
|
@ -52,7 +52,7 @@ public class MultiIconBuilder {
|
|||
* @return this builder (for chaining)
|
||||
*/
|
||||
public MultiIconBuilder addIcon(Icon icon, int w, int h, QUADRANT quandrant) {
|
||||
ImageIcon scaled = ResourceManager.getScaledIcon(icon, w, h);
|
||||
Icon scaled = ResourceManager.getScaledIcon(icon, w, h);
|
||||
|
||||
int x = (multiIcon.getIconWidth() - scaled.getIconWidth()) * quandrant.x;
|
||||
int y = (multiIcon.getIconHeight() - scaled.getIconHeight()) * quandrant.y;
|
||||
|
@ -75,7 +75,7 @@ public class MultiIconBuilder {
|
|||
* @return this builder (for chaining)
|
||||
*/
|
||||
public MultiIconBuilder addIcon(Icon icon, int w, int h, int x, int y) {
|
||||
ImageIcon scaled = ResourceManager.getScaledIcon(icon, w, h);
|
||||
Icon scaled = ResourceManager.getScaledIcon(icon, w, h);
|
||||
TranslateIcon txIcon = new TranslateIcon(scaled, x, y);
|
||||
multiIcon.addIcon(txIcon);
|
||||
return this;
|
||||
|
|
|
@ -338,7 +338,25 @@ public class ResourceManager {
|
|||
* @param height the height of the new icon
|
||||
* @return A new, scaled ImageIcon
|
||||
*/
|
||||
public static ImageIcon getScaledIcon(Icon icon, int width, int height) {
|
||||
public static ImageIcon getScaledIcon(ImageIcon icon, int width, int height) {
|
||||
return new ScaledImageIcon(icon, width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a scaled Icon from the given icon with scaling of
|
||||
* {@link Image#SCALE_AREA_AVERAGING}. If an EmptyIcon is passed, a new EmptyIcon is returned
|
||||
* with the new dimensions.
|
||||
*
|
||||
* @param icon the icon to scale
|
||||
* @param width the width of the new icon
|
||||
* @param height the height of the new icon
|
||||
* @return A new, scaled ImageIcon
|
||||
*/
|
||||
public static Icon getScaledIcon(Icon icon, int width, int height) {
|
||||
if (icon instanceof EmptyIcon) {
|
||||
return new EmptyIcon(width, height);
|
||||
}
|
||||
|
||||
return new ScaledImageIcon(icon, width, height);
|
||||
}
|
||||
|
||||
|
@ -478,7 +496,7 @@ public class ResourceManager {
|
|||
if (loadImage == null) {
|
||||
return null;
|
||||
}
|
||||
return getScaledIcon(loadImage, width, height);
|
||||
return (ImageIcon) getScaledIcon(loadImage, width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -514,6 +532,7 @@ public class ResourceManager {
|
|||
if (icon == null) {
|
||||
icon = doLoadIcon(filename);
|
||||
if (icon == null) {
|
||||
Msg.warn(ResourceManager.class, "Can't resolve icon: " + filename);
|
||||
icon = new UnresolvedIcon(filename, getDefaultIcon());
|
||||
}
|
||||
iconMap.put(filename, icon);
|
||||
|
|
|
@ -52,6 +52,10 @@ public class DerivedImageIcon extends LazyImageIcon {
|
|||
this.sourceImage = Objects.requireNonNull(image);
|
||||
}
|
||||
|
||||
public Icon getSourceIcon() {
|
||||
return sourceIcon;
|
||||
}
|
||||
|
||||
protected ImageIcon createImageIcon() {
|
||||
Image image = createImage();
|
||||
String imageName = getFilename();
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -18,29 +17,55 @@ package resources.icons;
|
|||
|
||||
import java.awt.Component;
|
||||
import java.awt.Graphics;
|
||||
import java.util.Objects;
|
||||
|
||||
import javax.swing.Icon;
|
||||
|
||||
public class EmptyIcon implements Icon {
|
||||
public class EmptyIcon implements Icon {
|
||||
|
||||
private int width;
|
||||
private int height;
|
||||
|
||||
public EmptyIcon( int width, int height ) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
public int getIconHeight() {
|
||||
return height;
|
||||
}
|
||||
private int width;
|
||||
private int height;
|
||||
|
||||
public int getIconWidth() {
|
||||
return width;
|
||||
}
|
||||
public EmptyIcon(int width, int height) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
public void paintIcon( Component c, Graphics g, int x, int y ) {
|
||||
// no-op
|
||||
}
|
||||
public int getIconHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
public int getIconWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
public void paintIcon(Component c, Graphics g, int x, int y) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(height, width);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
EmptyIcon other = (EmptyIcon) obj;
|
||||
return height == other.height && width == other.width;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "EmptyIcon(" + width + "," + height + ")";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -60,4 +60,20 @@ public class RotateIcon implements Icon {
|
|||
}
|
||||
return description;
|
||||
}
|
||||
|
||||
/**
|
||||
* The source icon being rotated.
|
||||
* @return the source icon being rotate
|
||||
*/
|
||||
public Icon getSourceIcon() {
|
||||
return icon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the rotation amount.
|
||||
* @return the rotation amount
|
||||
*/
|
||||
public int getRotation() {
|
||||
return degrees;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,4 +58,29 @@ public class TranslateIcon implements Icon {
|
|||
public String toString() {
|
||||
return getClass().getSimpleName() + "[" + ResourceManager.getIconName(icon) + "]";
|
||||
}
|
||||
|
||||
// for testing
|
||||
/**
|
||||
* Returns the icon that is being translated
|
||||
* @return the icon that is being translated
|
||||
*/
|
||||
public Icon getBaseIcon() {
|
||||
return icon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the amount the icon is being translated on the x axis;
|
||||
* @return the amount the icon is being translated on the x axis;
|
||||
*/
|
||||
public int getX() {
|
||||
return translateX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the amount the icon is being translated on the y axis;
|
||||
* @return the amount the icon is being translated on the y axis;
|
||||
*/
|
||||
public int getY() {
|
||||
return translateY;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ package generic.theme;
|
|||
import static org.junit.Assert.*;
|
||||
|
||||
import java.awt.Font;
|
||||
import java.text.ParseException;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
|
@ -25,12 +26,12 @@ public class FontModifierTest {
|
|||
private Font baseFont = new Font("Dialog", Font.PLAIN, 12);
|
||||
|
||||
@Test
|
||||
public void testNoModifiers() {
|
||||
public void testNoModifiers() throws ParseException {
|
||||
assertNull(FontModifier.parse(""));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSizeModifier() {
|
||||
public void testSizeModifier() throws ParseException {
|
||||
FontModifier modifier = FontModifier.parse("[6]");
|
||||
assertNotNull(modifier);
|
||||
Font newFont = modifier.modify(baseFont);
|
||||
|
@ -40,7 +41,7 @@ public class FontModifierTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testStyleModifierPlain() {
|
||||
public void testStyleModifierPlain() throws ParseException {
|
||||
FontModifier modifier = FontModifier.parse("[plain]");
|
||||
assertNotNull(modifier);
|
||||
Font newFont = modifier.modify(baseFont);
|
||||
|
@ -50,7 +51,7 @@ public class FontModifierTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testStyleModifierBold() {
|
||||
public void testStyleModifierBold() throws ParseException {
|
||||
FontModifier modifier = FontModifier.parse("[bold]");
|
||||
assertNotNull(modifier);
|
||||
Font newFont = modifier.modify(baseFont);
|
||||
|
@ -60,7 +61,7 @@ public class FontModifierTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testStyleModifierItalic() {
|
||||
public void testStyleModifierItalic() throws ParseException {
|
||||
FontModifier modifier = FontModifier.parse("[ITALIC]");
|
||||
assertNotNull(modifier);
|
||||
Font newFont = modifier.modify(baseFont);
|
||||
|
@ -70,7 +71,7 @@ public class FontModifierTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testStyleModifierBoldItalic() {
|
||||
public void testStyleModifierBoldItalic() throws ParseException {
|
||||
FontModifier modifier = FontModifier.parse("[BOLDitalic]");
|
||||
assertNotNull(modifier);
|
||||
Font newFont = modifier.modify(baseFont);
|
||||
|
@ -80,7 +81,7 @@ public class FontModifierTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testStyleModifierBoldItalic2() {
|
||||
public void testStyleModifierBoldItalic2() throws ParseException {
|
||||
FontModifier modifier = FontModifier.parse("[BOLD][italic]");
|
||||
assertNotNull(modifier);
|
||||
Font newFont = modifier.modify(baseFont);
|
||||
|
@ -90,7 +91,7 @@ public class FontModifierTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testFamilyModification() {
|
||||
public void testFamilyModification() throws ParseException {
|
||||
FontModifier modifier = FontModifier.parse("[monospaced]");
|
||||
assertNotNull(modifier);
|
||||
Font newFont = modifier.modify(baseFont);
|
||||
|
@ -100,7 +101,7 @@ public class FontModifierTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testSizeAndStyleModification() {
|
||||
public void testSizeAndStyleModification() throws ParseException {
|
||||
FontModifier modifier = FontModifier.parse("[16][bold]");
|
||||
assertNotNull(modifier);
|
||||
Font newFont = modifier.modify(baseFont);
|
||||
|
@ -116,7 +117,7 @@ public class FontModifierTest {
|
|||
FontModifier.parse("[monospaced][courier]");
|
||||
fail("Expecected Exception");
|
||||
}
|
||||
catch (IllegalStateException e) {
|
||||
catch (ParseException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
@ -127,7 +128,7 @@ public class FontModifierTest {
|
|||
FontModifier.parse("[plain][italic]");
|
||||
fail("Expected IllegalStateException");
|
||||
}
|
||||
catch (IllegalStateException e) {
|
||||
catch (ParseException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
@ -138,7 +139,7 @@ public class FontModifierTest {
|
|||
FontModifier.parse("asdfasf");
|
||||
fail("Expected IllegalArgumentExcption");
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
catch (ParseException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
@ -149,7 +150,7 @@ public class FontModifierTest {
|
|||
FontModifier.parse("[12]aa[13]");
|
||||
fail("Expected IllegalArgumentExcption");
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
catch (ParseException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
@ -160,7 +161,7 @@ public class FontModifierTest {
|
|||
FontModifier.parse("[12]aa13]");
|
||||
fail("Expected IllegalArgumentExcption");
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
catch (ParseException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
@ -171,7 +172,7 @@ public class FontModifierTest {
|
|||
FontModifier.parse("[12][plain]sz");
|
||||
fail("Expected IllegalArgumentExcption");
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
catch (ParseException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ package generic.theme;
|
|||
import static org.junit.Assert.*;
|
||||
|
||||
import java.awt.Font;
|
||||
import java.text.ParseException;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
@ -97,7 +98,7 @@ public class FontValueTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testParse() {
|
||||
public void testParse() throws ParseException {
|
||||
FontValue value = FontValue.parse("font.test", "Dialog-PLAIN-12");
|
||||
assertEquals("font.test", value.getId());
|
||||
assertEquals(FONT, value.getRawValue());
|
||||
|
|
|
@ -0,0 +1,189 @@
|
|||
/* ###
|
||||
* 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 static org.junit.Assert.*;
|
||||
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Point;
|
||||
import java.text.ParseException;
|
||||
|
||||
import javax.swing.Icon;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import resources.MultiIcon;
|
||||
import resources.ResourceManager;
|
||||
import resources.icons.RotateIcon;
|
||||
import resources.icons.TranslateIcon;
|
||||
|
||||
public class IconModifierTest {
|
||||
private Icon baseIcon = ResourceManager.getDefaultIcon();
|
||||
private GThemeValueMap values = new GThemeValueMap();
|
||||
|
||||
@Test
|
||||
public void testNoModifiers() throws Exception {
|
||||
assertNull(IconModifier.parse(""));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSizeModifier() throws Exception {
|
||||
IconModifier modifier = IconModifier.parse("[size(7,13)]");
|
||||
Icon modifiedIcon = modifier.modify(baseIcon, values);
|
||||
assertEquals(7, modifiedIcon.getIconWidth());
|
||||
assertEquals(13, modifiedIcon.getIconHeight());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSizeModifier2() throws Exception {
|
||||
IconModifier modifier = IconModifier.parse("[SIZE(7,13)]");
|
||||
Icon modifiedIcon = modifier.modify(baseIcon, values);
|
||||
assertEquals(7, modifiedIcon.getIconWidth());
|
||||
assertEquals(13, modifiedIcon.getIconHeight());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMoveModifier() throws Exception {
|
||||
IconModifier modifier = IconModifier.parse("[move(4, 3)]");
|
||||
Icon modifiedIcon = modifier.modify(baseIcon, values);
|
||||
assertTrue(modifiedIcon instanceof TranslateIcon);
|
||||
TranslateIcon translateIcon = (TranslateIcon) modifiedIcon;
|
||||
|
||||
assertEquals(4, translateIcon.getX());
|
||||
assertEquals(3, translateIcon.getY());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRotateModifier() throws Exception {
|
||||
IconModifier modifier = IconModifier.parse("[rotate(90)]");
|
||||
Icon modifiedIcon = modifier.modify(baseIcon, values);
|
||||
assertTrue(modifiedIcon instanceof RotateIcon);
|
||||
RotateIcon rotateIcon = (RotateIcon) modifiedIcon;
|
||||
|
||||
assertEquals(90, rotateIcon.getRotation());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDisabledModifier() throws Exception {
|
||||
IconModifier modifier = IconModifier.parse("[disabled]");
|
||||
Icon modifiedIcon = modifier.modify(baseIcon, values);
|
||||
assertNotEquals(baseIcon, modifiedIcon);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOverlayIcon() throws Exception {
|
||||
IconModifier modifier = IconModifier.parse("{images/flag.png}");
|
||||
Icon modifiedIcon = modifier.modify(baseIcon, values);
|
||||
assertTrue(modifiedIcon instanceof MultiIcon);
|
||||
MultiIcon multiIcon = (MultiIcon) modifiedIcon;
|
||||
Icon[] icons = multiIcon.getIcons();
|
||||
assertEquals(2, icons.length);
|
||||
assertEquals(baseIcon, icons[0]);
|
||||
assertEquals(ResourceManager.loadImage("images/flag.png"), icons[1]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOverlayIcon2() throws Exception {
|
||||
IconModifier modifier =
|
||||
IconModifier.parse("[size(20,25)]{images/flag.png[size(8,9)][move(4,4)]}");
|
||||
Icon modifiedIcon = modifier.modify(baseIcon, values);
|
||||
assertTrue(modifiedIcon instanceof MultiIcon);
|
||||
MultiIcon multiIcon = (MultiIcon) modifiedIcon;
|
||||
Icon[] icons = multiIcon.getIcons();
|
||||
assertEquals(2, icons.length);
|
||||
assertEquals(20, icons[0].getIconWidth());
|
||||
assertEquals(25, icons[0].getIconHeight());
|
||||
assertEquals(8, icons[1].getIconWidth());
|
||||
assertEquals(9, icons[1].getIconHeight());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidModifierString() {
|
||||
try {
|
||||
IconModifier.parse("dasdf");
|
||||
fail("Expected IllegalArgumentExcption");
|
||||
}
|
||||
catch (ParseException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidModifierString2() {
|
||||
try {
|
||||
IconModifier.parse("disabledx");
|
||||
fail("Expected IllegalArgumentExcption");
|
||||
}
|
||||
catch (ParseException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidModifierString3() {
|
||||
try {
|
||||
IconModifier.parse("[size(13,14,13)]");
|
||||
fail("Expected IllegalArgumentExcption");
|
||||
}
|
||||
catch (ParseException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidModifierString4() {
|
||||
try {
|
||||
IconModifier.parse("[size(14,12]");
|
||||
fail("Expected IllegalArgumentExcption");
|
||||
}
|
||||
catch (ParseException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidModifierString5() {
|
||||
try {
|
||||
IconModifier.parse("[size(14)]");
|
||||
fail("Expected IllegalArgumentExcption");
|
||||
}
|
||||
catch (ParseException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidModifierString6() {
|
||||
try {
|
||||
IconModifier.parse("[size(10,10)]move(3,4)]");
|
||||
fail("Expected IllegalArgumentExcption");
|
||||
}
|
||||
catch (ParseException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSerializationString() {
|
||||
//@formatter:off
|
||||
assertEquals("[size(5,9)]", new IconModifier(new Dimension(5,9), null, null, false).getSerializationString());
|
||||
assertEquals("[move(8,7)]", new IconModifier(null, new Point(8,7), null,false).getSerializationString());
|
||||
assertEquals("[disabled]", new IconModifier(null, null, null, true).getSerializationString());
|
||||
assertEquals("[size(5,0)][move(8,7)][disabled]", new IconModifier(new Dimension(5,0), new Point(8,7), null, true).getSerializationString());
|
||||
assertEquals("[rotate(90)]", new IconModifier(null, null, 90, false).getSerializationString());
|
||||
//@formatter:on
|
||||
}
|
||||
}
|
|
@ -17,12 +17,17 @@ package generic.theme;
|
|||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.text.ParseException;
|
||||
|
||||
import javax.swing.Icon;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import resources.MultiIcon;
|
||||
import resources.ResourceManager;
|
||||
import resources.icons.EmptyIcon;
|
||||
import resources.icons.TranslateIcon;
|
||||
|
||||
public class IconValueTest {
|
||||
private static Icon ICON1 = ResourceManager.getDefaultIcon();
|
||||
|
@ -99,7 +104,7 @@ public class IconValueTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testParse() {
|
||||
public void testParse() throws ParseException {
|
||||
IconValue value = IconValue.parse("icon.test", "images/core.png");
|
||||
assertEquals("icon.test", value.getId());
|
||||
assertEquals(ICON1, value.getRawValue());
|
||||
|
@ -116,6 +121,23 @@ public class IconValueTest {
|
|||
assertEquals("xyz.abc", value.getReferenceId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseWithOverlays() throws ParseException {
|
||||
IconValue value = IconValue.parse("icon.test",
|
||||
"images/core.png[size(25,25)]{images/flag.png[size(8,8)][move(4,4)]}");
|
||||
assertEquals("icon.test", value.getId());
|
||||
Icon icon = value.get(values);
|
||||
assertTrue(icon instanceof MultiIcon);
|
||||
MultiIcon multiIcon = (MultiIcon) icon;
|
||||
Icon[] icons = multiIcon.getIcons();
|
||||
assertEquals(2, icons.length);
|
||||
assertEquals(25, icons[0].getIconWidth());
|
||||
assertEquals(25, icons[0].getIconWidth());
|
||||
assertEquals(8, icons[1].getIconWidth());
|
||||
assertEquals(8, icons[1].getIconWidth());
|
||||
assertTrue(icons[1] instanceof TranslateIcon);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsIconKey() {
|
||||
assertTrue(IconValue.isIconKey("icon.a.b.c"));
|
||||
|
@ -150,4 +172,33 @@ public class IconValueTest {
|
|||
assertEquals("icon.parent", value.getReferenceId());
|
||||
assertNull(value.getRawValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseEmptyIcon() throws ParseException {
|
||||
IconValue value = IconValue.parse("icon.test", "EMPTY_ICON");
|
||||
assertEquals("icon.test", value.getId());
|
||||
Icon icon = value.get(values);
|
||||
assertEquals(new EmptyIcon(16, 16), icon);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseEmptyIconWithSize() throws ParseException {
|
||||
IconValue value = IconValue.parse("icon.test", "EMPTY_ICON[size(12,15)]");
|
||||
assertEquals("icon.test", value.getId());
|
||||
Icon icon = value.get(values);
|
||||
assertEquals(new EmptyIcon(12, 15), icon);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSerializationStringWithEmptyIcon() {
|
||||
IconValue value = new IconValue("icon.test", new EmptyIcon(16, 16));
|
||||
assertEquals("icon.test = EMPTY_ICON", value.getSerializationString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSerializationStringWithEmptyCustomSizeIcon() {
|
||||
IconValue value = new IconValue("icon.test", new EmptyIcon(22, 13));
|
||||
assertEquals("icon.test = EMPTY_ICON[size(22,13)]", value.getSerializationString());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ import javax.swing.Icon;
|
|||
|
||||
import org.junit.Test;
|
||||
|
||||
import resources.MultiIcon;
|
||||
import resources.ResourceManager;
|
||||
|
||||
public class ThemePropertyFileReaderTest {
|
||||
|
@ -49,12 +50,15 @@ public class ThemePropertyFileReaderTest {
|
|||
" font.a.b = (font.a.8[20][BOLD])",
|
||||
" icon.a.10 = core.png",
|
||||
" icon.a.11 = icon.a.10",
|
||||
" 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)]",
|
||||
"")));
|
||||
//@formatter:on
|
||||
|
||||
Color halfAlphaRed = new Color(0x80ff0000, true);
|
||||
GThemeValueMap values = reader.getDefaultValues();
|
||||
assertEquals(12, values.size());
|
||||
assertEquals(15, values.size());
|
||||
|
||||
assertEquals(WHITE, getColor(values, "color.b.1"));
|
||||
assertEquals(RED, getColor(values, "color.b.2"));
|
||||
|
@ -70,6 +74,16 @@ public class ThemePropertyFileReaderTest {
|
|||
|
||||
assertEquals(ResourceManager.loadImage("core.png"), getIcon(values, "icon.a.10"));
|
||||
assertEquals(ResourceManager.loadImage("core.png"), getIcon(values, "icon.a.11"));
|
||||
Icon icon = getIcon(values, "icon.a.12");
|
||||
assertEquals(17, icon.getIconWidth());
|
||||
assertEquals(21, icon.getIconHeight());
|
||||
|
||||
icon = getIcon(values, "icon.a.13");
|
||||
assertEquals(17, icon.getIconWidth());
|
||||
assertEquals(21, icon.getIconHeight());
|
||||
|
||||
icon = getIcon(values, "icon.a.14");
|
||||
assertTrue(icon instanceof MultiIcon);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
/* ###
|
||||
* 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 static org.junit.Assert.*;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class ThemeValueUtilsTest {
|
||||
@Test
|
||||
public void testParseGroupings() throws ParseException {
|
||||
String source = "(ab (cd))(ef)(( gh))";
|
||||
List<String> results = ThemeValueUtils.parseGroupings(source, '(', ')');
|
||||
assertEquals(3, results.size());
|
||||
assertEquals("ab (cd)", results.get(0));
|
||||
assertEquals("ef", results.get(1));
|
||||
assertEquals("( gh)", results.get(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseGroupingsParseError() {
|
||||
String source = "(ab (cd))(ef)( gh))";
|
||||
try {
|
||||
ThemeValueUtils.parseGroupings(source, '(', ')');
|
||||
fail("Expected parse Exception");
|
||||
}
|
||||
catch (ParseException e) {
|
||||
//expected
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseGroupingsParseError2() {
|
||||
String source = " xx";
|
||||
try {
|
||||
ThemeValueUtils.parseGroupings(source, '(', ')');
|
||||
fail("Expected parse Exception");
|
||||
}
|
||||
catch (ParseException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue