mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-06 03:50:02 +02:00
GP-1981 - Checkpoint - Initial Theming
This commit is contained in:
parent
38292de02c
commit
f808431251
108 changed files with 4355 additions and 738 deletions
|
@ -36,7 +36,7 @@ class CursorBackgroundColorModel implements ListingBackgroundColorModel {
|
|||
private AddressIndexMap addressIndexMap;
|
||||
|
||||
@AutoOptionConsumed(category = {}, name = GhidraOptions.HIGHLIGHT_CURSOR_LINE_COLOR)
|
||||
private Color cursorColor = GhidraOptions.DEFAULT_CURSOR_LINE_COLOR;
|
||||
private Color cursorLineColor = GhidraOptions.DEFAULT_CURSOR_LINE_COLOR;
|
||||
@AutoOptionConsumed(category = {}, name = GhidraOptions.HIGHLIGHT_CURSOR_LINE)
|
||||
private boolean doHighlight = true;
|
||||
@SuppressWarnings("unused")
|
||||
|
@ -61,7 +61,7 @@ class CursorBackgroundColorModel implements ListingBackgroundColorModel {
|
|||
if (!Objects.equals(cursorAddress, address)) {
|
||||
return defaultBackgroundColor;
|
||||
}
|
||||
return cursorColor;
|
||||
return cursorLineColor;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -16,6 +16,10 @@ data/ElfFunctionsThatDoNotReturn||GHIDRA||||END|
|
|||
data/ExtensionPoint.manifest||GHIDRA||||END|
|
||||
data/MachOFunctionsThatDoNotReturn||GHIDRA||||END|
|
||||
data/PEFunctionsThatDoNotReturn||GHIDRA||||END|
|
||||
data/base.functiongraph.theme.properties||GHIDRA||||END|
|
||||
data/base.listing.theme.properties||GHIDRA||||END|
|
||||
data/base.theme.properties||GHIDRA||||END|
|
||||
data/decompiler.theme.properties||GHIDRA||||END|
|
||||
data/file_extension_icons.xml||GHIDRA||||END|
|
||||
data/functionTags.xml||GHIDRA||||END|
|
||||
data/ms_pe_rich_products.xml||GHIDRA||||END|
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
[Defaults]
|
||||
|
||||
|
||||
color.bg.functiongraph = color.bg
|
||||
|
||||
color.bg.functiongraph.vertex.group = rgb(226, 255, 155)
|
||||
color.bg.functiongraph.vertex.entry = color.palette.lightgreen
|
||||
color.bg.functiongraph.vertex.exit = color.palette.lightred
|
||||
color.bg.functiongraph.vertex.picked = color.palette.yellow
|
||||
|
||||
color.bg.functiongraph.edge.fall-through = color.flowtype.fall-through
|
||||
color.bg.functiongraph.edge.fall-through.highlight = rgb(255, 127, 127)
|
||||
color.bg.functiongraph.edge.jump.conditional = color.flowtype.jump.conditional
|
||||
color.bg.functiongraph.edge.jump.conditional.highlight = green
|
||||
color.bg.functiongraph.edge.jump.unconditional = color.flowtype.jump.unconditional
|
||||
color.bg.functiongraph.edge.jump.unconditional.highlight = rgb(127, 127, 255)
|
||||
|
||||
|
||||
|
||||
[Dark Defaults]
|
||||
|
||||
|
||||
color.bg.functiongraph.vertex.group = rgb(226, 222, 179) // TODO confirm value
|
||||
|
||||
color.bg.functiongraph.edge.fall-through.highlight = rgb(165, 76, 80)
|
||||
color.bg.functiongraph.edge.jump.conditional.highlight = rgb(95, 160, 196)
|
||||
color.bg.functiongraph.edge.jump.unconditional.highlight = rgb(140, 162, 88)
|
||||
|
||||
|
||||
|
||||
|
109
Ghidra/Features/Base/data/base.listing.theme.properties
Normal file
109
Ghidra/Features/Base/data/base.listing.theme.properties
Normal file
|
@ -0,0 +1,109 @@
|
|||
[Defaults]
|
||||
|
||||
color.bg.listing = color.bg
|
||||
color.bg.currentline.listing = color.bg.currentline
|
||||
color.bg.selection.listing = color.bg.selection
|
||||
color.bg.highlight.listing = color.bg.highlight
|
||||
|
||||
|
||||
color.cursor.focused.listing = color.cursor.focused
|
||||
color.cursor.unfocused.listing = color.cursor.unfocused
|
||||
|
||||
color.fg.listing.address = color.fg
|
||||
color.fg.listing.ref.bad = red
|
||||
color.fg.listing.bytes = blue
|
||||
color.fg.listing.constant = turquoise
|
||||
color.fg.listing.label.unreferenced = black
|
||||
color.fg.listing.entrypoint = magenta
|
||||
color.fg.listing.comment.auto = lightGray
|
||||
color.fg.listing.comment.eol = blue
|
||||
color.fg.listing.comment.repeatable = darkOrange
|
||||
color.fg.listing.comment.ref-repeatable = comflowerBlue
|
||||
color.fg.listing.comment.plate = gray
|
||||
color.fg.listing.comment.post = blue
|
||||
color.fg.listing.comment.pre = indigo
|
||||
color.fg.listing.ref.ext.resolved = teal
|
||||
color.fg.listing.fieldname = color.fg
|
||||
color.fg.listing.function.callfixup = fuchsia
|
||||
color.fg.listing.function.name = blue
|
||||
color.fg.listing.function.param = black
|
||||
color.fg.listing.function.tag = mediumVioletRed
|
||||
color.fg.listing.function.param.auto = gray
|
||||
color.fg.listing.function.return-type = black
|
||||
color.fg.listing.function.param.custom = indigo
|
||||
color.fg.listing.function.param.dynamic = teal
|
||||
color.fg.listing.label.local = green
|
||||
color.fg.listing.label.non-primary = olive
|
||||
color.fg.listing.label.primary = darkBlue
|
||||
color.fg.listing.mnemonic.override = deepPink
|
||||
color.fg.listing.mnemonic = navy
|
||||
color.fg.listing.mnemonic.unimplemented = navy
|
||||
color.fg.listing.flow-arrow.inactive = lightGray
|
||||
color.fg.listing.flow-arrow.active = color.fg
|
||||
color.fg.listing.flow-arrow.selected = limeGreen
|
||||
color.fg.listing.separator = color.fg
|
||||
color.fg.listing.variable = purple
|
||||
color.fg.listing.version-tracking = purple
|
||||
color.fg.listing.xref = darkGreen
|
||||
color.fg.listing.xref.offcut = gray
|
||||
color.fg.listing.xref.read = blue
|
||||
color.fg.listing.xref.write = darkOrange
|
||||
color.fg.listing.xref.other = color.fg
|
||||
color.fg.listing.register = olive
|
||||
color.fg.listing.underline = comflowerBlue
|
||||
color.fg.listing.pcode.label = blue
|
||||
color.fg.listing.pcode.space = blue
|
||||
color.fg.listing.pcode.varnode = blue
|
||||
color.fg.listing.pcode.userop = blue
|
||||
|
||||
|
||||
|
||||
[Dark Defaults]
|
||||
|
||||
#color.fg.listing.address = color.fg
|
||||
#color.fg.listing.ref.bad = red
|
||||
#color.fg.listing.bytes = blue
|
||||
#color.fg.listing.constant = turquoise
|
||||
#color.fg.listing.label.unreferenced = black
|
||||
#color.fg.listing.entrypoint = magenta
|
||||
#color.fg.listing.comment.auto = rgb(95,129,157)
|
||||
#color.fg.listing.comment.eol = blue
|
||||
#color.fg.listing.comment.repeatable = darkOrange
|
||||
#color.fg.listing.comment.ref-repeatable = comflowerBlue
|
||||
#color.fg.listing.comment.plate = gray
|
||||
#color.fg.listing.comment.post = blue
|
||||
#color.fg.listing.comment.pre = indigo
|
||||
#color.fg.listing.ref.ext.resolved = teal
|
||||
#color.fg.listing.fieldname = color.fg
|
||||
#color.fg.listing.function.callfixup = fuchsia
|
||||
#color.fg.listing.function.name = blue
|
||||
#color.fg.listing.function.param = black
|
||||
#color.fg.listing.function.tag = mediumVioletRed
|
||||
#color.fg.listing.function.param.auto = gray
|
||||
#color.fg.listing.function.return-type = black
|
||||
#color.fg.listing.function.param.custom = indigo
|
||||
#color.fg.listing.function.param.dynamic = teal
|
||||
#color.fg.listing.label.local = green
|
||||
#color.fg.listing.label.non-primary = olive
|
||||
#color.fg.listing.label.primary = darkBlue
|
||||
#color.fg.listing.mnemonic.override = deepPink
|
||||
#color.fg.listing.mnemonic = lightSlateGray
|
||||
#color.fg.listing.mnemonic.unimplemented = navy
|
||||
#color.fg.listing.flow-arrow.inactive = lightGray
|
||||
#color.fg.listing.flow-arrow.active = color.fg
|
||||
#color.fg.listing.flow-arrow.selected = limeGreen
|
||||
#color.fg.listing.separator = color.fg
|
||||
#color.fg.listing.variable = purple
|
||||
#color.fg.listing.version-tracking = purple
|
||||
#color.fg.listing.xref = darkGreen
|
||||
#color.fg.listing.xref.offcut = gray
|
||||
#color.fg.listing.xref.read = blue
|
||||
#color.fg.listing.xref.write = darkOrange
|
||||
#color.fg.listing.xref.other = color.fg
|
||||
#color.fg.listing.register = olive
|
||||
#color.fg.listing.underline = comflowerBlue
|
||||
#color.fg.listing.pcode.label = blue
|
||||
#color.fg.listing.pcode.space = blue
|
||||
#color.fg.listing.pcode.varnode = blue
|
||||
#color.fg.listing.pcode.userop = blue
|
||||
|
66
Ghidra/Features/Base/data/base.theme.properties
Normal file
66
Ghidra/Features/Base/data/base.theme.properties
Normal file
|
@ -0,0 +1,66 @@
|
|||
[Defaults]
|
||||
|
||||
color.bg.undefined = rgb(220, 220, 220) // bg for clients displaying undefined functions
|
||||
|
||||
color.flowtype.fall-through = red
|
||||
color.flowtype.jump.conditional = #007C00 // dark green
|
||||
color.flowtype.jump.unconditional = blue
|
||||
|
||||
color.bg.table.selection.bundle = lightSkyBlue
|
||||
color.fg.table.selection.bundle = black
|
||||
color.fg.table.bundle.disabled = darkGray
|
||||
color.fg.table.bundle.error = red
|
||||
color.fg.table.bundle.busy = gray
|
||||
color.fg.table.bundle.inactive = black
|
||||
color.fg.table.bundle.active = green
|
||||
|
||||
color.bg.splash.infopanel = color.bg
|
||||
|
||||
color.bg.table.selected.ghidratable = color.bg
|
||||
color.fg.table.ghidratable.equate.bad = red
|
||||
color.fg.table.ghidratable.equate = blue
|
||||
color.fg.table.ghidratable.suggestion = silver
|
||||
|
||||
color.fg.consoletextpane = color.fg
|
||||
color.fg.error.consoletextpane = color.fg.error
|
||||
|
||||
color.fg.infopanel.version = color.fg
|
||||
|
||||
color.fg.interpreterpanel = color.fg
|
||||
color.fg.interpreterpanel.error = color.fg.error
|
||||
color.fg.listing.highlighter.default = yellow
|
||||
color.fg.listing.highlighter.scoped-read = rgb(204,204, 0)
|
||||
color.fg.listing.highlighter.scoped-write = green
|
||||
|
||||
color.bg.markerservice = color.bg
|
||||
|
||||
color.bg.search.highlight = rgb(255,255,200)
|
||||
color.bg.search.current-line.highlight = yellow
|
||||
|
||||
[Dark Defaults]
|
||||
|
||||
color.bg = rgb(40, 42, 46) // TODO this should be in a more generic module
|
||||
|
||||
color.flowtype.fall-through = rgb(164, 66, 66)
|
||||
color.flowtype.jump.conditional = rgb(95, 129, 157)
|
||||
color.flowtype.jump.unconditional = rgb(140, 148, 64)
|
||||
|
||||
|
||||
color.bg.table.selection.bundle = darkBlue
|
||||
color.fg.table.selection.bundle = gray
|
||||
color.fg.table.bundle.disabled = lightGray
|
||||
color.fg.table.bundle.error = indianRed
|
||||
color.fg.table.bundle.busy = gray
|
||||
color.fg.table.bundle.inactive = lightGray
|
||||
color.fg.table.bundle.active = limeGreen
|
||||
|
||||
|
||||
color.fg.table.ghidratable.equate.bad = indianRed
|
||||
color.fg.table.ghidratable.equate = royalBlue
|
||||
color.fg.table.ghidratable.suggestion = darkGray
|
||||
|
||||
color.bg.search.highlight = rgb(189,183,107)
|
||||
color.bg.search.current-line.highlight = gold
|
||||
|
||||
color.fg.listing.highlighter.scoped-read = rgb(100,100, 0)
|
||||
color.fg.listing.highlighter.scoped-write = forestGreen
|
31
Ghidra/Features/Base/data/decompiler.theme.properties
Normal file
31
Ghidra/Features/Base/data/decompiler.theme.properties
Normal file
|
@ -0,0 +1,31 @@
|
|||
[Defaults]
|
||||
color.bg.decompiler = color.bg
|
||||
color.fg.decompiler = color.fg
|
||||
|
||||
color.fg.decompiler.keyword = #0001e6
|
||||
color.fg.decompiler.function.name = blue
|
||||
color.fg.decompiler.comment = blueViolet
|
||||
color.fg.decompiler.variable = #999900 // close to oliveDrab
|
||||
color.fg.decompiler.constant = forestGreen
|
||||
color.fg.decompiler.type = mediumBlue
|
||||
color.fg.decompiler.parameter = darkMagenta
|
||||
color.fg.decompiler.global = darkCyan
|
||||
|
||||
color.bg.decompiler.middle-mouse = rgba(255,255,0,.5)
|
||||
color.bg.decompiler.current-variable = rgba(255,255,0,0.5)
|
||||
|
||||
[Dark Defaults]
|
||||
|
||||
color.fg.decompiler.keyword = peru
|
||||
color.fg.decompiler.function.name = cadetBlue
|
||||
color.fg.decompiler.comment = lightSlateGray
|
||||
color.fg.decompiler.variable = #999900 // close to oliveDrab
|
||||
color.fg.decompiler.constant = forestGreen
|
||||
color.fg.decompiler.type = blue
|
||||
color.fg.decompiler.parameter = darkMagenta
|
||||
color.fg.decompiler.global = darkCyan
|
||||
|
||||
|
||||
color.bg.decompiler.middle-mouse = rgb(55,59,65)
|
||||
color.bg.decompiler.current-variable = rgb(55, 59, 65)
|
||||
|
|
@ -18,6 +18,7 @@ package ghidra;
|
|||
import java.awt.Color;
|
||||
import java.awt.event.MouseEvent;
|
||||
|
||||
import docking.theme.GColor;
|
||||
import ghidra.framework.options.Options;
|
||||
|
||||
/**
|
||||
|
@ -156,7 +157,7 @@ public interface GhidraOptions {
|
|||
|
||||
final String HIGHLIGHT_CURSOR_LINE_COLOR = "Cursor." + HIGHLIGHT_CURSOR_LINE_COLOR_OPTION_NAME;
|
||||
|
||||
final Color DEFAULT_CURSOR_LINE_COLOR = new Color(232, 242, 254);
|
||||
final Color DEFAULT_CURSOR_LINE_COLOR = new GColor("color.bg.currentline.listing");
|
||||
|
||||
final String HIGHLIGHT_CURSOR_LINE_OPTION_NAME = "Highlight Cursor Line";
|
||||
|
||||
|
@ -176,6 +177,7 @@ public interface GhidraOptions {
|
|||
|
||||
public static enum CURSOR_MOUSE_BUTTON_NAMES {
|
||||
LEFT(MouseEvent.BUTTON1), MIDDLE(MouseEvent.BUTTON2), RIGHT(MouseEvent.BUTTON3);
|
||||
|
||||
private int mouseEventID;
|
||||
|
||||
CURSOR_MOUSE_BUTTON_NAMES(int mouseEventID) {
|
||||
|
@ -190,10 +192,9 @@ public interface GhidraOptions {
|
|||
// end cursor highlight
|
||||
|
||||
final String OPTION_SELECTION_COLOR = "Selection Colors.Selection Color";
|
||||
final Color DEFAULT_SELECTION_COLOR = new Color(180, 255, 180);
|
||||
final Color DEFAULT_SELECTION_COLOR = new GColor("color.bg.selection.listing");
|
||||
|
||||
final String OPTION_HIGHLIGHT_COLOR = "Selection Colors.Highlight Color";
|
||||
final Color DEFAULT_HIGHLIGHT_COLOR = new Color(255, 255, 180);
|
||||
final Color DEFAULT_HIGHLIGHT_COLOR = new GColor("color.bg.highlight.listing");
|
||||
final String APPLY_ENABLED = "apply.enabled";
|
||||
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ import javax.swing.event.ChangeEvent;
|
|||
import javax.swing.event.ChangeListener;
|
||||
|
||||
import docking.action.DockingAction;
|
||||
import docking.theme.GColor;
|
||||
import docking.widgets.fieldpanel.*;
|
||||
import docking.widgets.fieldpanel.field.Field;
|
||||
import docking.widgets.fieldpanel.support.FieldLocation;
|
||||
|
@ -61,12 +62,17 @@ public abstract class AbstractCodeBrowserPlugin<P extends CodeViewerProvider> ex
|
|||
implements CodeViewerService, CodeFormatService, OptionsChangeListener, FormatModelListener,
|
||||
DomainObjectListener, CodeBrowserPluginInterface {
|
||||
|
||||
private static final Color CURSOR_LINE_COLOR = GhidraOptions.DEFAULT_CURSOR_LINE_COLOR;
|
||||
private static final String CURSOR_COLOR = "Cursor.Cursor Color - Focused";
|
||||
private static final String UNFOCUSED_CURSOR_COLOR = "Cursor.Cursor Color - Unfocused";
|
||||
private static final String BLINK_CURSOR = "Cursor.Blink Cursor";
|
||||
private static final String MOUSE_WHEEL_HORIZONTAL_SCROLLING = "Mouse.Horizontal Scrolling";
|
||||
|
||||
//@formatter:off
|
||||
public static final Color IFOCUSED_CURSOR_COLOR = new GColor("color.cursor.focused.listing");
|
||||
public static final Color IUNFOCUSED_CURSOR_COLOR = new GColor("color.cursor.unfocused.listing");
|
||||
public static final Color ICURRENT_LINE_HIGHLIGHT_COLOR = new GColor("color.bg.currentline.listing");
|
||||
//@formatter:on
|
||||
|
||||
// - Icon -
|
||||
private ImageIcon CURSOR_LOC_ICON =
|
||||
ResourceManager.loadImage("images/cursor_arrow_flipped.gif");
|
||||
|
@ -554,14 +560,15 @@ public abstract class AbstractCodeBrowserPlugin<P extends CodeViewerProvider> ex
|
|||
GhidraOptions.DEFAULT_HIGHLIGHT_COLOR, helpLocation,
|
||||
"The highlight color in the browser.");
|
||||
|
||||
fieldOptions.registerOption(CURSOR_COLOR, Color.RED, helpLocation,
|
||||
fieldOptions.registerOption(CURSOR_COLOR, IFOCUSED_CURSOR_COLOR, helpLocation,
|
||||
"The color of the cursor in the browser.");
|
||||
fieldOptions.registerOption(UNFOCUSED_CURSOR_COLOR, Color.PINK, helpLocation,
|
||||
fieldOptions.registerOption(UNFOCUSED_CURSOR_COLOR, IUNFOCUSED_CURSOR_COLOR, helpLocation,
|
||||
"The color of the cursor in the browser when the browser does not have focus.");
|
||||
fieldOptions.registerOption(BLINK_CURSOR, true, helpLocation,
|
||||
"When selected, the cursor will blink when the containing window is focused.");
|
||||
fieldOptions.registerOption(GhidraOptions.HIGHLIGHT_CURSOR_LINE_COLOR, CURSOR_LINE_COLOR,
|
||||
helpLocation, "The background color of the line where the cursor is located");
|
||||
fieldOptions.registerOption(GhidraOptions.HIGHLIGHT_CURSOR_LINE_COLOR,
|
||||
ICURRENT_LINE_HIGHLIGHT_COLOR, helpLocation,
|
||||
"The background color of the line where the cursor is located");
|
||||
fieldOptions.registerOption(GhidraOptions.HIGHLIGHT_CURSOR_LINE, true, helpLocation,
|
||||
"Toggles highlighting background color of line containing the cursor");
|
||||
|
||||
|
@ -588,10 +595,10 @@ public abstract class AbstractCodeBrowserPlugin<P extends CodeViewerProvider> ex
|
|||
highlightMarkers.setMarkerColor(color);
|
||||
}
|
||||
|
||||
color = fieldOptions.getColor(CURSOR_COLOR, Color.RED);
|
||||
color = fieldOptions.getColor(CURSOR_COLOR, IFOCUSED_CURSOR_COLOR);
|
||||
fieldPanel.setFocusedCursorColor(color);
|
||||
|
||||
color = fieldOptions.getColor(UNFOCUSED_CURSOR_COLOR, Color.PINK);
|
||||
color = fieldOptions.getColor(UNFOCUSED_CURSOR_COLOR, IUNFOCUSED_CURSOR_COLOR);
|
||||
fieldPanel.setNonFocusCursorColor(color);
|
||||
|
||||
Boolean isBlinkCursor = fieldOptions.getBoolean(BLINK_CURSOR, true);
|
||||
|
@ -601,8 +608,8 @@ public abstract class AbstractCodeBrowserPlugin<P extends CodeViewerProvider> ex
|
|||
fieldOptions.getBoolean(MOUSE_WHEEL_HORIZONTAL_SCROLLING, true);
|
||||
fieldPanel.setHorizontalScrollingEnabled(horizontalScrollingEnabled);
|
||||
|
||||
cursorHighlightColor =
|
||||
fieldOptions.getColor(GhidraOptions.HIGHLIGHT_CURSOR_LINE_COLOR, CURSOR_LINE_COLOR);
|
||||
cursorHighlightColor = fieldOptions.getColor(GhidraOptions.HIGHLIGHT_CURSOR_LINE_COLOR,
|
||||
ICURRENT_LINE_HIGHLIGHT_COLOR);
|
||||
|
||||
isHighlightCursorLine = fieldOptions.getBoolean(GhidraOptions.HIGHLIGHT_CURSOR_LINE, true);
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ import java.util.regex.Pattern;
|
|||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import docking.theme.GColor;
|
||||
import docking.widgets.fieldpanel.field.FieldElement;
|
||||
import docking.widgets.fieldpanel.support.FieldLocation;
|
||||
import docking.widgets.fieldpanel.support.Highlight;
|
||||
|
@ -51,18 +52,15 @@ import ghidra.util.datastruct.Stack;
|
|||
|
||||
public class ListingHighlightProvider
|
||||
implements ButtonPressedListener, OptionsChangeListener, HighlightProvider {
|
||||
|
||||
private static final String DISPLAY_HIGHLIGHT_NAME =
|
||||
CURSOR_HIGHLIGHT_GROUP + DELIMITER + "Enabled";
|
||||
|
||||
private static final String SCOPED_WRITE_HIGHLIGHT_COLOR =
|
||||
CURSOR_HIGHLIGHT_GROUP + DELIMITER + "Scoped Write Highlight Color";
|
||||
|
||||
private static final String SCOPED_READ_HIGHLIGHT_COLOR =
|
||||
CURSOR_HIGHLIGHT_GROUP + DELIMITER + "Scoped Read Highlight Color";
|
||||
|
||||
private static final String SCOPE_REGISTER_OPERAND =
|
||||
CURSOR_HIGHLIGHT_GROUP + DELIMITER + "Scope Register Operand";
|
||||
//@formatter:off
|
||||
private static final Color DEFAULT_HIGHLIGHT_COLOR = new GColor("color.fg.listing.highlighter.default");
|
||||
private static final Color DEFAULT_SCOPED_READ_COLOR = new GColor("color.fg.listing.highlighter.scoped-read");
|
||||
private static final Color DEFAULT_SCOPED_WRITE_COLOR = new GColor("color.fg.listing.highlighter.scoped-write");
|
||||
private static final String DISPLAY_HIGHLIGHT_NAME = CURSOR_HIGHLIGHT_GROUP + DELIMITER + "Enabled";
|
||||
private static final String SCOPED_WRITE_HIGHLIGHT_COLOR = CURSOR_HIGHLIGHT_GROUP + DELIMITER + "Scoped Write Highlight Color";
|
||||
private static final String SCOPED_READ_HIGHLIGHT_COLOR = CURSOR_HIGHLIGHT_GROUP + DELIMITER + "Scoped Read Highlight Color";
|
||||
private static final String SCOPE_REGISTER_OPERAND = CURSOR_HIGHLIGHT_GROUP + DELIMITER + "Scope Register Operand";
|
||||
//@formatter:on
|
||||
|
||||
private static char[] UNDERSCORE_AND_PERIOD_OK = new char[] { '.', '_' };
|
||||
private static char[] UNDERSCORE_OK = new char[] { '_' };
|
||||
|
@ -871,11 +869,11 @@ public class ListingHighlightProvider
|
|||
ToolOptions opt = tool.getOptions(CATEGORY_BROWSER_FIELDS);
|
||||
HelpLocation hl = new HelpLocation("CodeBrowserPlugin", "Cursor_Text_Highlight");
|
||||
|
||||
opt.registerOption(HIGHLIGHT_COLOR_NAME, Color.YELLOW, hl,
|
||||
opt.registerOption(HIGHLIGHT_COLOR_NAME, DEFAULT_HIGHLIGHT_COLOR, hl,
|
||||
"The color to use to highlight text.");
|
||||
opt.registerOption(SCOPED_WRITE_HIGHLIGHT_COLOR, new Color(204, 204, 0), hl,
|
||||
opt.registerOption(SCOPED_WRITE_HIGHLIGHT_COLOR, DEFAULT_SCOPED_WRITE_COLOR, hl,
|
||||
"The color to use for showing a register being written.");
|
||||
opt.registerOption(SCOPED_READ_HIGHLIGHT_COLOR, new Color(0, 255, 0), hl,
|
||||
opt.registerOption(SCOPED_READ_HIGHLIGHT_COLOR, DEFAULT_SCOPED_READ_COLOR, hl,
|
||||
"The color to use for showing a register being read.");
|
||||
|
||||
opt.registerOption(SCOPE_REGISTER_OPERAND, true, hl,
|
||||
|
@ -895,11 +893,11 @@ public class ListingHighlightProvider
|
|||
setHighlightString(null, null);
|
||||
}
|
||||
|
||||
textMatchingHighlightColor = opt.getColor(HIGHLIGHT_COLOR_NAME, Color.YELLOW);
|
||||
|
||||
textMatchingHighlightColor = opt.getColor(HIGHLIGHT_COLOR_NAME, DEFAULT_HIGHLIGHT_COLOR);
|
||||
scopeWriteHighlightColor =
|
||||
opt.getColor(SCOPED_WRITE_HIGHLIGHT_COLOR, new Color(204, 204, 0));
|
||||
scopeReadHighlightColor = opt.getColor(SCOPED_READ_HIGHLIGHT_COLOR, new Color(0, 255, 0));
|
||||
opt.getColor(SCOPED_WRITE_HIGHLIGHT_COLOR, DEFAULT_SCOPED_WRITE_COLOR);
|
||||
scopeReadHighlightColor =
|
||||
opt.getColor(SCOPED_READ_HIGHLIGHT_COLOR, DEFAULT_SCOPED_READ_COLOR);
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ package ghidra.app.plugin.core.codebrowser;
|
|||
import java.awt.Color;
|
||||
import java.math.BigInteger;
|
||||
|
||||
import docking.theme.GColor;
|
||||
import docking.widgets.fieldpanel.support.BackgroundColorModel;
|
||||
import ghidra.app.services.MarkerService;
|
||||
import ghidra.app.util.viewer.listingpanel.ListingBackgroundColorModel;
|
||||
|
@ -33,7 +34,7 @@ public class MarkerServiceBackgroundColorModel implements ListingBackgroundColor
|
|||
private MarkerService markerService;
|
||||
private Program program;
|
||||
private AddressIndexMap indexMap;
|
||||
private Color defaultBackgroundColor = Color.WHITE;
|
||||
private Color defaultBackgroundColor = new GColor("color.bg.markerservice");
|
||||
|
||||
public MarkerServiceBackgroundColorModel(MarkerService markerService, Program program,
|
||||
AddressIndexMap indexMap) {
|
||||
|
|
|
@ -28,6 +28,7 @@ import javax.swing.text.*;
|
|||
|
||||
import docking.DockingUtils;
|
||||
import docking.actions.KeyBindingUtils;
|
||||
import docking.theme.GColor;
|
||||
import generic.util.WindowUtilities;
|
||||
import ghidra.app.plugin.core.console.CodeCompletion;
|
||||
import ghidra.framework.options.OptionsChangeListener;
|
||||
|
@ -37,6 +38,7 @@ import ghidra.util.*;
|
|||
|
||||
public class InterpreterPanel extends JPanel implements OptionsChangeListener {
|
||||
|
||||
private static final String COLOR_ID = "interpreterpanel.color";
|
||||
private static final String COMPLETION_WINDOW_TRIGGER_LABEL = "Completion Window Trigger";
|
||||
private static final String COMPLETION_WINDOW_TRIGGER_DESCRIPTION =
|
||||
"The key binding used to show the auto-complete window " +
|
||||
|
@ -46,8 +48,8 @@ public class InterpreterPanel extends JPanel implements OptionsChangeListener {
|
|||
"This is the font that will be used in the Console. " +
|
||||
"Double-click the font example to change it.";
|
||||
|
||||
private static final Color NORMAL_COLOR = Color.black;
|
||||
private static final Color ERROR_COLOR = Color.red;
|
||||
private static final Color NORMAL_COLOR = new GColor("color.fg.interpreterpanel");
|
||||
private static final Color ERROR_COLOR = new GColor("color.fg.interpreterpanel.error");
|
||||
|
||||
public enum TextType {
|
||||
STDOUT, STDERR, STDIN;
|
||||
|
|
|
@ -15,17 +15,18 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.osgi;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Dimension;
|
||||
import java.io.File;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.TableModelEvent;
|
||||
|
||||
import docking.action.builder.ActionBuilder;
|
||||
import docking.theme.GColor;
|
||||
import docking.widgets.filechooser.GhidraFileChooser;
|
||||
import docking.widgets.filechooser.GhidraFileChooserMode;
|
||||
import docking.widgets.table.GTable;
|
||||
|
@ -105,8 +106,8 @@ public class BundleStatusComponentProvider extends ComponentProviderAdapter {
|
|||
|
||||
bundleStatusTable = new GTable(bundleStatusTableModel);
|
||||
bundleStatusTable.setName("BUNDLESTATUS_TABLE");
|
||||
bundleStatusTable.setSelectionBackground(new Color(204, 204, 255));
|
||||
bundleStatusTable.setSelectionForeground(Color.BLACK);
|
||||
bundleStatusTable.setSelectionBackground(new GColor("color.bg.table.selection.bundle"));
|
||||
bundleStatusTable.setSelectionForeground(new GColor("color.fg.table.selection.bundle"));
|
||||
bundleStatusTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
|
||||
|
||||
// give actions a chance to update status when selection changed
|
||||
|
|
|
@ -27,6 +27,7 @@ import javax.swing.event.TableModelEvent;
|
|||
|
||||
import org.osgi.framework.Bundle;
|
||||
|
||||
import docking.theme.GColor;
|
||||
import docking.widgets.table.*;
|
||||
import generic.jar.ResourceFile;
|
||||
import generic.util.Path;
|
||||
|
@ -42,11 +43,14 @@ import ghidra.util.table.column.GColumnRenderer;
|
|||
*/
|
||||
public class BundleStatusTableModel
|
||||
extends GDynamicColumnTableModel<BundleStatus, List<BundleStatus>> {
|
||||
private static final Color COLOR_BUNDLE_ERROR = Color.RED;
|
||||
private static final Color COLOR_BUNDLE_DISABLED = Color.DARK_GRAY;
|
||||
private static final Color COLOR_BUNDLE_BUSY = Color.GRAY;
|
||||
private static final Color COLOR_BUNDLE_INACTIVE = Color.BLACK;
|
||||
private static final Color COLOR_BUNDLE_ACTIVE = new Color(0.0f, .6f, 0.0f); // a dark green
|
||||
|
||||
//@formatter:off
|
||||
private static final Color COLOR_BUNDLE_ERROR = new GColor("color.fg.table.bundle.error");
|
||||
private static final Color COLOR_BUNDLE_DISABLED = new GColor("color.fg.table.bundle.disabled");
|
||||
private static final Color COLOR_BUNDLE_BUSY = new GColor("color.fg.table.bundle.busy");
|
||||
private static final Color COLOR_BUNDLE_INACTIVE = new GColor("color.fg.table.bundle.inactive");
|
||||
private static final Color COLOR_BUNDLE_ACTIVE = new GColor("color.fg.table.bundle.active");
|
||||
//@formatter:on
|
||||
|
||||
private BundleHost bundleHost;
|
||||
private BundleStatusComponentProvider provider;
|
||||
|
|
|
@ -49,8 +49,7 @@ public class CodeUnitPrintable implements Printable {
|
|||
private static final PaintContext PAINT_CONTEXT = new PaintContext();
|
||||
static {
|
||||
PAINT_CONTEXT.setForegroundColor(Color.BLACK);
|
||||
PAINT_CONTEXT.setDefaultBackgroundColor(Color.WHITE);
|
||||
PAINT_CONTEXT.setBackgroundColor(Color.white);
|
||||
PAINT_CONTEXT.setBackgroundColor(Color.WHITE);
|
||||
PAINT_CONTEXT.setCursorColor(Color.RED);
|
||||
PAINT_CONTEXT.setSelectionColor(new Color(180, 255, 180));
|
||||
PAINT_CONTEXT.setHighlightColor(new Color(255, 255, 150));
|
||||
|
|
|
@ -1,167 +0,0 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.gui;
|
||||
|
||||
import static ghidra.docking.util.DockingWindowsLookAndFeelUtils.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import docking.options.editor.StringWithChoicesEditor;
|
||||
import docking.tool.ToolConstants;
|
||||
import docking.widgets.OptionDialog;
|
||||
import ghidra.app.CorePluginPackage;
|
||||
import ghidra.app.plugin.PluginCategoryNames;
|
||||
import ghidra.docking.util.DockingWindowsLookAndFeelUtils;
|
||||
import ghidra.framework.main.ApplicationLevelOnlyPlugin;
|
||||
import ghidra.framework.main.FrontEndTool;
|
||||
import ghidra.framework.options.*;
|
||||
import ghidra.framework.plugintool.*;
|
||||
import ghidra.framework.plugintool.util.PluginStatus;
|
||||
import ghidra.framework.preferences.Preferences;
|
||||
import ghidra.util.*;
|
||||
|
||||
//@formatter:off
|
||||
@PluginInfo(
|
||||
status = PluginStatus.RELEASED,
|
||||
packageName = CorePluginPackage.NAME,
|
||||
category = PluginCategoryNames.SUPPORT,
|
||||
shortDescription = "Sets the GUI look and feel",
|
||||
description = "Adds a Tool Option to allow the user to adjust the GUI look and feel settings. " +
|
||||
"Ghidra may have to be restarted to see the effect. This plugin is available " +
|
||||
"only in the Ghidra Project Window."
|
||||
)
|
||||
//@formatter:on
|
||||
public class LookAndFeelPlugin extends Plugin implements ApplicationLevelOnlyPlugin, OptionsChangeListener {
|
||||
|
||||
private String selectedLookAndFeel;
|
||||
private boolean useInvertedColors;
|
||||
public final static String LOOK_AND_FEEL_NAME = "Swing Look And Feel";
|
||||
private final static String USE_INVERTED_COLORS_NAME = "Use Inverted Colors";
|
||||
private final static String OPTIONS_TITLE = ToolConstants.TOOL_OPTIONS;
|
||||
|
||||
private static boolean issuedLafNotification;
|
||||
private static boolean issuedPreferredDarkThemeLafNotification;
|
||||
|
||||
public LookAndFeelPlugin(PluginTool tool) {
|
||||
super(tool);
|
||||
|
||||
SystemUtilities.assertTrue(tool instanceof FrontEndTool,
|
||||
"Plugin added to the wrong type of tool");
|
||||
initLookAndFeelOptions();
|
||||
}
|
||||
|
||||
private void initLookAndFeelOptions() {
|
||||
|
||||
ToolOptions opt = tool.getOptions(OPTIONS_TITLE);
|
||||
|
||||
selectedLookAndFeel = getInstalledLookAndFeelName();
|
||||
List<String> lookAndFeelNames = getLookAndFeelNames();
|
||||
opt.registerOption(LOOK_AND_FEEL_NAME, OptionType.STRING_TYPE, selectedLookAndFeel,
|
||||
new HelpLocation(ToolConstants.TOOL_HELP_TOPIC, "Look_And_Feel"),
|
||||
"Set the look and feel for Ghidra. After you change the " +
|
||||
"look and feel, you will have to restart Ghidra to see the effect.",
|
||||
new StringWithChoicesEditor(lookAndFeelNames));
|
||||
selectedLookAndFeel = opt.getString(LOOK_AND_FEEL_NAME, selectedLookAndFeel);
|
||||
|
||||
useInvertedColors = getUseInvertedColorsPreference();
|
||||
opt.registerOption(USE_INVERTED_COLORS_NAME, OptionType.BOOLEAN_TYPE, useInvertedColors,
|
||||
new HelpLocation(ToolConstants.TOOL_HELP_TOPIC, "Use_Inverted_Colors"),
|
||||
"Indicates to invert all drawn colors. This provides the ability to create an " +
|
||||
"effective 'Dark Theme' appearance. (Note: you may have to change your " +
|
||||
"Look and Feel to achieve the best rendering.)\n\n" +
|
||||
"<FONT SIZE='7'><B>PROTOTYPE</B></FONT> - This feature is an example prototype " +
|
||||
"and contains many known rendering issues that cause some widgets to be " +
|
||||
"<U>unreadable</U>.");
|
||||
useInvertedColors = opt.getBoolean(USE_INVERTED_COLORS_NAME, useInvertedColors);
|
||||
|
||||
opt.addOptionsChangeListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void optionsChanged(ToolOptions options, String optionName, Object oldValue,
|
||||
Object newValue) {
|
||||
|
||||
if (optionName.equals(LOOK_AND_FEEL_NAME)) {
|
||||
String newLookAndFeel = (String) newValue;
|
||||
if (!newLookAndFeel.equals(selectedLookAndFeel)) {
|
||||
issueLaFNotification();
|
||||
}
|
||||
|
||||
saveLookAndFeel((String) newValue);
|
||||
}
|
||||
|
||||
if (optionName.equals(USE_INVERTED_COLORS_NAME)) {
|
||||
boolean newUseInvertedColors = (Boolean) newValue;
|
||||
if (newUseInvertedColors != useInvertedColors) {
|
||||
|
||||
issueLaFNotification();
|
||||
}
|
||||
|
||||
useInvertedColors = newUseInvertedColors;
|
||||
Preferences.setProperty(USE_INVERTED_COLORS_KEY, Boolean.toString(useInvertedColors));
|
||||
Preferences.store();
|
||||
|
||||
if (useInvertedColors) {
|
||||
issuePreferredDarkThemeLaFNotification();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void saveLookAndFeel(String lookAndFeelName) {
|
||||
selectedLookAndFeel = lookAndFeelName;
|
||||
Preferences.setProperty(LAST_LOOK_AND_FEEL_KEY, selectedLookAndFeel);
|
||||
Preferences.store();
|
||||
}
|
||||
|
||||
private void issuePreferredDarkThemeLaFNotification() {
|
||||
if (issuedPreferredDarkThemeLafNotification) {
|
||||
return;
|
||||
}
|
||||
|
||||
issuedPreferredDarkThemeLafNotification = true;
|
||||
|
||||
if (DockingWindowsLookAndFeelUtils.METAL_LOOK_AND_FEEL.equals(selectedLookAndFeel)) {
|
||||
return;
|
||||
}
|
||||
|
||||
int choice = OptionDialog.showYesNoDialog(null, "Change Look and Feel?", "The '" +
|
||||
USE_INVERTED_COLORS_NAME + "' setting works best with the " +
|
||||
"'Metal' Look and Feel.\nWould you like to switch to that Look and Feel upon restart?");
|
||||
if (choice == OptionDialog.YES_OPTION) {
|
||||
SystemUtilities.runSwingLater(() -> {
|
||||
|
||||
saveLookAndFeel(DockingWindowsLookAndFeelUtils.METAL_LOOK_AND_FEEL);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void issueLaFNotification() {
|
||||
if (issuedLafNotification) {
|
||||
return;
|
||||
}
|
||||
|
||||
issuedLafNotification = true;
|
||||
Msg.showInfo(getClass(), null, "Look And Feel Updated",
|
||||
"The new Look and Feel will take effect \nafter you exit and restart Ghidra.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
ToolOptions opt = tool.getOptions(OPTIONS_TITLE);
|
||||
opt.removeOptionsChangeListener(this);
|
||||
super.dispose();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,154 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.gui;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import docking.action.builder.ActionBuilder;
|
||||
import docking.options.editor.StringWithChoicesEditor;
|
||||
import docking.theme.GTheme;
|
||||
import docking.theme.GThemeDialog;
|
||||
import docking.theme.Gui;
|
||||
import docking.tool.ToolConstants;
|
||||
import ghidra.app.CorePluginPackage;
|
||||
import ghidra.app.plugin.PluginCategoryNames;
|
||||
import ghidra.docking.util.LookAndFeelUtils;
|
||||
import ghidra.framework.main.FrontEndOnly;
|
||||
import ghidra.framework.main.FrontEndTool;
|
||||
import ghidra.framework.options.OptionType;
|
||||
import ghidra.framework.options.OptionsChangeListener;
|
||||
import ghidra.framework.options.ToolOptions;
|
||||
import ghidra.framework.plugintool.Plugin;
|
||||
import ghidra.framework.plugintool.PluginInfo;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.framework.plugintool.util.PluginStatus;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.SystemUtilities;
|
||||
|
||||
//@formatter:off
|
||||
@PluginInfo(
|
||||
status = PluginStatus.RELEASED,
|
||||
packageName = CorePluginPackage.NAME,
|
||||
category = PluginCategoryNames.SUPPORT,
|
||||
shortDescription = "Manages themes for the Ghdira GUI",
|
||||
description = "Adds actions and options to manage Themes within Ghidra. " +
|
||||
"This plugin is available only in the Ghidra Project Window."
|
||||
)
|
||||
//@formatter:on
|
||||
public class ThemeManagerPlugin extends Plugin implements FrontEndOnly, OptionsChangeListener {
|
||||
|
||||
public final static String THEME_OPTIONS_NAME = "Theme";
|
||||
private final static String OPTIONS_TITLE = ToolConstants.TOOL_OPTIONS;
|
||||
|
||||
private boolean issuedRestartNotification;
|
||||
// private static boolean issuedPreferredDarkThemeLafNotification;
|
||||
|
||||
public ThemeManagerPlugin(PluginTool tool) {
|
||||
super(tool);
|
||||
|
||||
SystemUtilities.assertTrue(tool instanceof FrontEndTool,
|
||||
"Plugin added to the wrong type of tool");
|
||||
initThemeOptions();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void init() {
|
||||
new ActionBuilder("Dump UI Properties", getName())
|
||||
.menuPath("Edit", "Dump UI Properies")
|
||||
.onAction(e -> LookAndFeelUtils.dumpUIProperties())
|
||||
.buildAndInstall(tool);
|
||||
|
||||
new ActionBuilder("Show Properties", getName())
|
||||
.menuPath("Edit", "Theme Properties")
|
||||
.onAction(e -> showThemeProperties())
|
||||
.buildAndInstall(tool);
|
||||
|
||||
}
|
||||
|
||||
private void showThemeProperties() {
|
||||
GThemeDialog dialog = new GThemeDialog();
|
||||
tool.showDialog(dialog);
|
||||
}
|
||||
|
||||
private void initThemeOptions() {
|
||||
|
||||
ToolOptions opt = tool.getOptions(OPTIONS_TITLE);
|
||||
|
||||
GTheme activeTheme = Gui.getActiveTheme();
|
||||
List<String> themeNames = getAllThemeNames();
|
||||
|
||||
opt.registerOption(THEME_OPTIONS_NAME, OptionType.STRING_TYPE, activeTheme.getName(),
|
||||
new HelpLocation(ToolConstants.TOOL_HELP_TOPIC, "Look_And_Feel"),
|
||||
"Set the look and feel for Ghidra. After you change the " +
|
||||
"look and feel, you will have to restart Ghidra to see the effect.",
|
||||
new StringWithChoicesEditor(themeNames));
|
||||
|
||||
opt.addOptionsChangeListener(this);
|
||||
}
|
||||
|
||||
private List<String> getAllThemeNames() {
|
||||
Set<GTheme> allThemes = Gui.getAllThemes();
|
||||
List<String> themeNames =
|
||||
allThemes.stream().map(t -> t.getName()).collect(Collectors.toList());
|
||||
Collections.sort(themeNames);
|
||||
return themeNames;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void optionsChanged(ToolOptions options, String optionName, Object oldValue,
|
||||
Object newValue) {
|
||||
|
||||
if (optionName.equals(THEME_OPTIONS_NAME)) {
|
||||
String newThemeName = (String) newValue;
|
||||
if (!newThemeName.equals(Gui.getActiveTheme().getName())) {
|
||||
issueRestartNeededMessage();
|
||||
}
|
||||
|
||||
saveLookAndFeel((String) newValue);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void saveLookAndFeel(String themeName) {
|
||||
Set<GTheme> allThemes = Gui.getAllThemes();
|
||||
for (GTheme theme : allThemes) {
|
||||
if (theme.getName().equals(themeName)) {
|
||||
Gui.saveThemeToPreferneces(theme);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void issueRestartNeededMessage() {
|
||||
if (issuedRestartNotification) {
|
||||
return;
|
||||
}
|
||||
|
||||
issuedRestartNotification = true;
|
||||
Msg.showInfo(getClass(), null, "Look And Feel Updated",
|
||||
"The new Look and Feel will take effect \nafter you exit and restart Ghidra.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
ToolOptions opt = tool.getOptions(OPTIONS_TITLE);
|
||||
opt.removeOptionsChangeListener(this);
|
||||
super.dispose();
|
||||
}
|
||||
}
|
|
@ -17,6 +17,8 @@ package ghidra.app.util;
|
|||
|
||||
import java.awt.Color;
|
||||
|
||||
import docking.theme.GColor;
|
||||
|
||||
/**
|
||||
* Miscellaneous defined constants
|
||||
*
|
||||
|
@ -94,11 +96,13 @@ public interface PluginConstants {
|
|||
/**
|
||||
* Color for highlighting for searches.
|
||||
*/
|
||||
public static final Color SEARCH_HIGHLIGHT_COLOR = new Color(255, 255, 200);
|
||||
public static final Color SEARCH_HIGHLIGHT_COLOR = new GColor("color.bg.search.highlight");
|
||||
|
||||
/**
|
||||
* Default highlight color used when something to highlight is at the current
|
||||
* address.
|
||||
*/
|
||||
public static final Color SEARCH_HIGHLIGHT_CURRENT_ADDR_COLOR = Color.YELLOW;
|
||||
public static final Color SEARCH_HIGHLIGHT_CURRENT_ADDR_COLOR =
|
||||
new GColor("color.bg.search.current-line.highlight");
|
||||
|
||||
}
|
||||
|
|
|
@ -148,17 +148,17 @@ public class SetEquateDialog extends DialogComponentProvider {
|
|||
int refCount = eqRowObject.getRefCount();
|
||||
if (refCount > 0) {
|
||||
if (eqRowObject.getEntryName().contains(EquateManager.ERROR_TAG)) {
|
||||
c.setForeground(isSelected ? Color.WHITE : Color.RED);
|
||||
c.setForeground(isSelected ? this.SELECTED_CELL_COLOR : this.BAD_EQUATE_COLOR);
|
||||
}
|
||||
else {
|
||||
Equate e = eqRowObject.getEquate();
|
||||
if (e != null && !e.isEnumBased()) {
|
||||
c.setForeground(isSelected ? Color.WHITE : Color.BLUE.brighter());
|
||||
c.setForeground(isSelected ? this.SELECTED_CELL_COLOR : this.EQUATE_COLOR);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
c.setForeground(isSelected ? Color.WHITE : Color.GRAY.darker());
|
||||
c.setForeground(isSelected ? this.SELECTED_CELL_COLOR : this.SUGGESTION_COLOR);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ import java.math.BigInteger;
|
|||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import docking.theme.GColor;
|
||||
import docking.widgets.fieldpanel.support.BackgroundColorModel;
|
||||
import ghidra.app.util.viewer.util.AddressIndexMap;
|
||||
import ghidra.framework.model.DomainObjectChangedEvent;
|
||||
|
@ -40,7 +41,7 @@ public class PropertyBasedBackgroundColorModel
|
|||
public static final String COLOR_PROPERTY_NAME = "LISTING_COLOR";
|
||||
private IntRangeMap colorMap;
|
||||
private AddressIndexMap indexMap;
|
||||
private Color defaultBackgroundColor = Color.WHITE;
|
||||
private Color defaultBackgroundColor = new GColor("color.bg.listing");
|
||||
private Map<Integer, Color> colorCache = new HashMap<>();
|
||||
private Program program;
|
||||
private boolean enabled = false;
|
||||
|
|
|
@ -27,6 +27,7 @@ import java.util.stream.IntStream;
|
|||
import javax.swing.*;
|
||||
import javax.swing.border.Border;
|
||||
|
||||
import docking.theme.GColor;
|
||||
import docking.widgets.checkbox.GCheckBox;
|
||||
import docking.widgets.combobox.GComboBox;
|
||||
import docking.widgets.fieldpanel.*;
|
||||
|
@ -43,95 +44,150 @@ import ghidra.util.SystemUtilities;
|
|||
*/
|
||||
public class OptionsGui extends JPanel {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Color DARK_GREEN = new Color(0, 128, 0);
|
||||
private static final Color BLUE_GREEN = new Color(0, 128, 64);
|
||||
private static final Color DARK_BLUE = new Color(0, 0, 128);
|
||||
private static final Color PALE_BLUE = new Color(128, 128, 255);
|
||||
private static final Color YELLOW_ORANGE = new Color(155, 150, 50);
|
||||
private static final Color PURPLE = new Color(155, 50, 155);
|
||||
private static final Color DEEP_PURPLE = new Color(75, 0, 130);
|
||||
private static final Color DARK_PURPLE = new Color(102, 0, 102);
|
||||
private static final Color DARK_CYAN = new Color(0, 102, 102);
|
||||
private static final Color DARK_ORANGE = new Color(255, 128, 0);
|
||||
private static final Color DARK_RED = new Color(130, 0, 75);
|
||||
private static final Highlight[] NO_HIGHLIGHTS = new Highlight[0];
|
||||
private static final HighlightFactory hlFactory =
|
||||
(field, text, cursorTextOffset) -> NO_HIGHLIGHTS;
|
||||
|
||||
public static final ScreenElement BACKGROUND =
|
||||
new ScreenElement("Background", new GColor("color.bg.listing"));
|
||||
public static final ScreenElement COMMENT_AUTO =
|
||||
new ScreenElement("Comment, Automatic", Color.LIGHT_GRAY);
|
||||
public static final ScreenElement ADDRESS = new ScreenElement("Address", Color.BLACK);
|
||||
public static final ScreenElement BACKGROUND = new ScreenElement("Background", Color.WHITE);
|
||||
new ScreenElement("Comment, Automatic", new GColor("color.fg.listing.comment.auto"));
|
||||
public static final ScreenElement ADDRESS =
|
||||
new ScreenElement("Address", new GColor("color.fg.listing.address"));
|
||||
|
||||
public static final ScreenElement BAD_REF_ADDR =
|
||||
new ScreenElement("Bad Reference Address", Color.RED);
|
||||
public static final ScreenElement BYTES = new ScreenElement("Bytes", Color.BLUE);
|
||||
public static final ScreenElement CONSTANT = new ScreenElement("Constant", BLUE_GREEN);
|
||||
public static final ScreenElement LABELS_UNREFD =
|
||||
new ScreenElement("Labels, Unreferenced", Color.BLACK);
|
||||
public static final ScreenElement ENTRY_POINT = new ScreenElement("Entry Point", Color.MAGENTA);
|
||||
public static final ScreenElement COMMENT_EOL =
|
||||
new ScreenElement("Comment, EOL", "EOL Comment", Color.BLUE);
|
||||
public static final ScreenElement EXT_REF_RESOLVED =
|
||||
new ScreenElement("External Reference, Resolved", Color.CYAN.darker().darker());
|
||||
public static final ScreenElement FIELD_NAME = new ScreenElement("Field Name", Color.BLACK);
|
||||
new ScreenElement("Bad Reference Address", new GColor("color.fg.listing.ref.bad"));
|
||||
|
||||
public static final ScreenElement BYTES =
|
||||
new ScreenElement("Bytes", new GColor("color.fg.listing.bytes"));
|
||||
|
||||
public static final ScreenElement CONSTANT =
|
||||
new ScreenElement("Constant", new GColor("color.fg.listing.constant"));
|
||||
|
||||
public static final ScreenElement LABELS_UNREFD = new ScreenElement("Labels, Unreferenced",
|
||||
new GColor("color.fg.listing.label.unreferenced"));
|
||||
|
||||
public static final ScreenElement ENTRY_POINT =
|
||||
new ScreenElement("Entry Point", new GColor("color.fg.listing.entrypoint"));
|
||||
|
||||
public static final ScreenElement COMMENT_EOL = new ScreenElement("Comment, EOL", "EOL Comment",
|
||||
new GColor("color.fg.listing.comment.auto"));
|
||||
|
||||
public static final ScreenElement EXT_REF_RESOLVED = new ScreenElement(
|
||||
"External Reference, Resolved", new GColor("color.fg.listing.ref.ext.resolved"));
|
||||
|
||||
public static final ScreenElement FIELD_NAME =
|
||||
new ScreenElement("Field Name", new GColor("color.fg.listing.fieldname"));
|
||||
|
||||
public static final ScreenElement FUN_CALL_FIXUP =
|
||||
new ScreenElement("Function Call-Fixup", new Color(255, 0, 204));
|
||||
public static final ScreenElement FUN_NAME = new ScreenElement("Function Name", Color.BLUE);
|
||||
new ScreenElement("Function Call-Fixup", new GColor("color.fg.listing.function.callfixup"));
|
||||
|
||||
public static final ScreenElement FUN_NAME =
|
||||
new ScreenElement("Function Name", new GColor("color.fg.listing.function.name"));
|
||||
|
||||
public static final ScreenElement FUN_PARAMS =
|
||||
new ScreenElement("Function Parameters", Color.BLACK);
|
||||
public static final ScreenElement FUN_TAG = new ScreenElement("Function Tag", DARK_RED);
|
||||
public static final ScreenElement FUN_AUTO_PARAMS =
|
||||
new ScreenElement("Function Auto-Parameters", Color.GRAY);
|
||||
public static final ScreenElement FUN_RET_TYPE =
|
||||
new ScreenElement("Function Return Type", Color.BLACK);
|
||||
new ScreenElement("Function Parameters", new GColor("color.fg.listing.function.param"));
|
||||
|
||||
public static final ScreenElement FUN_TAG =
|
||||
new ScreenElement("Function Tag", new GColor("color.fg.listing.function.tag"));
|
||||
|
||||
public static final ScreenElement FUN_AUTO_PARAMS = new ScreenElement(
|
||||
"Function Auto-Parameters", new GColor("color.fg.listing.function.param.auto"));
|
||||
|
||||
public static final ScreenElement FUN_RET_TYPE = new ScreenElement("Function Return Type",
|
||||
new GColor("color.fg.listing.function.return-type"));
|
||||
|
||||
public static final ScreenElement COMMENT_REPEATABLE =
|
||||
new ScreenElement("Comment, Repeatable", DARK_ORANGE);
|
||||
public static final ScreenElement COMMENT_REF_REPEAT =
|
||||
new ScreenElement("Comment, Referenced Repeatable", new Color(190, 190, 255));
|
||||
public static final ScreenElement LABELS_LOCAL = new ScreenElement("Labels, Local", BLUE_GREEN);
|
||||
new ScreenElement("Comment, Repeatable", new GColor("color.fg.listing.comment.repeatable"));
|
||||
|
||||
public static final ScreenElement COMMENT_REF_REPEAT = new ScreenElement(
|
||||
"Comment, Referenced Repeatable", new GColor("color.fg.listing.comment.ref-repeatable"));
|
||||
|
||||
public static final ScreenElement LABELS_LOCAL =
|
||||
new ScreenElement("Labels, Local", new GColor("color.fg.listing.label.local"));
|
||||
|
||||
public static final ScreenElement MNEMONIC_OVERRIDE =
|
||||
new ScreenElement("Mnemonic, Override", new Color(255, 0, 204));
|
||||
public static final ScreenElement MNEMONIC = new ScreenElement("Mnemonic", DARK_BLUE);
|
||||
public static final ScreenElement UNIMPL =
|
||||
new ScreenElement("Unimplemented Mnemonic", Color.RED);
|
||||
public static final ScreenElement FLOW_ARROW_NON_ACTIVE =
|
||||
new ScreenElement("Flow Arrow, Not Active", new Color(160, 160, 160));
|
||||
new ScreenElement("Mnemonic, Override", new GColor("color.fg.listing.mnemonic.override"));
|
||||
|
||||
public static final ScreenElement MNEMONIC =
|
||||
new ScreenElement("Mnemonic", new GColor("color.fg.listing.mnemonic"));
|
||||
|
||||
public static final ScreenElement UNIMPL = new ScreenElement("Unimplemented Mnemonic",
|
||||
new GColor("color.fg.listing.mnemonic.unimplemented"));
|
||||
|
||||
public static final ScreenElement FLOW_ARROW_NON_ACTIVE = new ScreenElement(
|
||||
"Flow Arrow, Not Active", new GColor("color.fg.listing.flow-arrow.inactive"));
|
||||
|
||||
public static final ScreenElement FLOW_ARROW_ACTIVE =
|
||||
new ScreenElement("Flow Arrow, Active", Color.BLACK);
|
||||
public static final ScreenElement FLOW_ARROW_SELECTED =
|
||||
new ScreenElement("Flow Arrow, Selected", new Color(0, 200, 0));
|
||||
new ScreenElement("Flow Arrow, Active", new GColor("color.fg.listing.flow-arrow.active"));
|
||||
|
||||
public static final ScreenElement FLOW_ARROW_SELECTED = new ScreenElement(
|
||||
"Flow Arrow, Selected", new GColor("color.fg.listing.flow-arrow.selected"));
|
||||
|
||||
public static final ScreenElement LABELS_NON_PRIMARY =
|
||||
new ScreenElement("Labels, Non-primary", YELLOW_ORANGE);
|
||||
public static final ScreenElement COMMENT_PLATE =
|
||||
new ScreenElement("Comment, Plate", "Plate Comment", Color.GRAY);
|
||||
public static final ScreenElement COMMENT_POST =
|
||||
new ScreenElement("Comment, Post", "Post-Comment", Color.BLUE);
|
||||
public static final ScreenElement COMMENT_PRE =
|
||||
new ScreenElement("Comment, Pre", "Pre-Comment", DEEP_PURPLE);
|
||||
new ScreenElement("Labels, Non-primary", new GColor("color.fg.listing.label.non-primary"));
|
||||
|
||||
public static final ScreenElement COMMENT_PLATE = new ScreenElement("Comment, Plate",
|
||||
"Plate Comment", new GColor("color.fg.listing.comment.plate"));
|
||||
|
||||
public static final ScreenElement COMMENT_POST = new ScreenElement("Comment, Post",
|
||||
"Post-Comment", new GColor("color.fg.listing.comment.post"));
|
||||
|
||||
public static final ScreenElement COMMENT_PRE = new ScreenElement("Comment, Pre", "Pre-Comment",
|
||||
new GColor("color.fg.listing.comment.pre"));
|
||||
|
||||
public static final ScreenElement LABELS_PRIMARY =
|
||||
new ScreenElement("Labels, Primary", DARK_BLUE);
|
||||
public static final ScreenElement SEPARATOR = new ScreenElement("Separator", Color.BLACK);
|
||||
public static final ScreenElement VARIABLE = new ScreenElement("Variable", PURPLE);
|
||||
public static final ScreenElement PARAMETER_CUSTOM =
|
||||
new ScreenElement("Parameter, Custom Storage", DARK_PURPLE);
|
||||
public static final ScreenElement PARAMETER_DYNAMIC =
|
||||
new ScreenElement("Parameter, Dynamic Storage", DARK_CYAN);
|
||||
public static final ScreenElement VERSION_TRAK = new ScreenElement("Version Track", PURPLE);
|
||||
public static final ScreenElement XREF = new ScreenElement("XRef", DARK_GREEN);
|
||||
public static final ScreenElement XREF_OFFCUT = new ScreenElement("XRef, Offcut", Color.GRAY);
|
||||
public static final ScreenElement XREF_READ = new ScreenElement("XRef Read", Color.BLUE);
|
||||
public static final ScreenElement XREF_WRITE = new ScreenElement("XRef Write", DARK_ORANGE);
|
||||
public static final ScreenElement XREF_OTHER = new ScreenElement("XRef Other", Color.BLACK);
|
||||
public static final ScreenElement REGISTERS = new ScreenElement("Registers", YELLOW_ORANGE);
|
||||
public static final ScreenElement UNDERLINE = new ScreenElement("Underline", PALE_BLUE);
|
||||
new ScreenElement("Labels, Primary", new GColor("color.fg.listing.label.primary"));
|
||||
|
||||
public static final ScreenElement SEPARATOR =
|
||||
new ScreenElement("Separator", new GColor("color.fg.listing.separator"));
|
||||
|
||||
public static final ScreenElement VARIABLE =
|
||||
new ScreenElement("Variable", new GColor("color.fg.listing.variable"));
|
||||
|
||||
public static final ScreenElement PARAMETER_CUSTOM = new ScreenElement(
|
||||
"Parameter, Custom Storage", new GColor("color.fg.listing.function.param.custom"));
|
||||
|
||||
public static final ScreenElement PARAMETER_DYNAMIC = new ScreenElement(
|
||||
"Parameter, Dynamic Storage", new GColor("color.fg.listing.function.param.dynamic"));
|
||||
|
||||
public static final ScreenElement VERSION_TRAK =
|
||||
new ScreenElement("Version Track", new GColor("color.fg.listing.version-tracking"));
|
||||
|
||||
public static final ScreenElement XREF =
|
||||
new ScreenElement("XRef", new GColor("color.fg.listing.xref"));
|
||||
|
||||
public static final ScreenElement XREF_OFFCUT =
|
||||
new ScreenElement("XRef, Offcut", new GColor("color.fg.listing.xref.offcut"));
|
||||
|
||||
public static final ScreenElement XREF_READ =
|
||||
new ScreenElement("XRef Read", new GColor("color.fg.listing.xref.read"));
|
||||
|
||||
public static final ScreenElement XREF_WRITE =
|
||||
new ScreenElement("XRef Write", new GColor("color.fg.listing.xref.write"));
|
||||
|
||||
public static final ScreenElement XREF_OTHER =
|
||||
new ScreenElement("XRef Other", new GColor("color.fg.listing.xref.other"));
|
||||
|
||||
public static final ScreenElement REGISTERS =
|
||||
new ScreenElement("Registers", new GColor("color.fg.listing.register"));
|
||||
|
||||
public static final ScreenElement UNDERLINE =
|
||||
new ScreenElement("Underline", new GColor("color.fg.listing.underline"));
|
||||
|
||||
public static final ScreenElement PCODE_LINE_LABEL =
|
||||
new ScreenElement("P-code Line Label", Color.BLUE);
|
||||
new ScreenElement("P-code Line Label", new GColor("color.fg.listing.pcode.label"));
|
||||
|
||||
public static final ScreenElement PCODE_ADDR_SPACE =
|
||||
new ScreenElement("P-code Address Space", Color.BLUE);
|
||||
new ScreenElement("P-code Address Space", new GColor("color.fg.listing.pcode.space"));
|
||||
|
||||
public static final ScreenElement PCODE_RAW_VARNODE =
|
||||
new ScreenElement("P-code Raw Varnode", Color.BLUE);
|
||||
new ScreenElement("P-code Raw Varnode", new GColor("color.fg.listing.pcode.varnode"));
|
||||
|
||||
public static final ScreenElement PCODE_USEROP =
|
||||
new ScreenElement("P-code Userop", Color.BLUE);
|
||||
new ScreenElement("P-code Userop", new GColor("color.fg.listing.pcode.userop"));
|
||||
|
||||
//@formatter:on
|
||||
|
||||
static ScreenElement[] elements = { ADDRESS, BACKGROUND, BAD_REF_ADDR, BYTES, COMMENT_AUTO,
|
||||
COMMENT_EOL, COMMENT_PLATE, COMMENT_POST, COMMENT_PRE, COMMENT_REPEATABLE,
|
||||
|
|
|
@ -15,16 +15,12 @@
|
|||
*/
|
||||
package ghidra.framework;
|
||||
|
||||
import java.awt.Taskbar;
|
||||
import java.awt.Toolkit;
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import docking.DockingErrorDisplay;
|
||||
import docking.DockingWindowManager;
|
||||
import docking.framework.ApplicationInformationDisplayFactory;
|
||||
import docking.framework.SplashScreen;
|
||||
import docking.theme.Gui;
|
||||
import docking.widgets.PopupKeyStorePasswordProvider;
|
||||
import ghidra.docking.util.DockingWindowsLookAndFeelUtils;
|
||||
import ghidra.formats.gfilesystem.crypto.CryptoProviders;
|
||||
import ghidra.formats.gfilesystem.crypto.PopupGUIPasswordProvider;
|
||||
import ghidra.framework.main.GhidraApplicationInformationDisplayFactory;
|
||||
|
@ -46,10 +42,7 @@ public class GhidraApplicationConfiguration extends HeadlessGhidraApplicationCon
|
|||
|
||||
@Override
|
||||
protected void initializeApplication() {
|
||||
|
||||
DockingWindowsLookAndFeelUtils.loadFromPreferences();
|
||||
|
||||
platformSpecificFixups();
|
||||
Gui.initialize();
|
||||
|
||||
if (showSplashScreen) {
|
||||
showUserAgreement();
|
||||
|
@ -63,17 +56,6 @@ public class GhidraApplicationConfiguration extends HeadlessGhidraApplicationCon
|
|||
CryptoProviders.getInstance().registerCryptoProvider(new PopupGUIPasswordProvider());
|
||||
}
|
||||
|
||||
private static void platformSpecificFixups() {
|
||||
|
||||
// Set the dock icon for macOS
|
||||
if (Taskbar.isTaskbarSupported()) {
|
||||
Taskbar taskbar = Taskbar.getTaskbar();
|
||||
if (taskbar.isSupported(Taskbar.Feature.ICON_IMAGE)) {
|
||||
taskbar.setIconImage(ApplicationInformationDisplayFactory.getLargestWindowIcon());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void showUserAgreement() {
|
||||
String value = Preferences.getProperty(USER_AGREEMENT_PROPERTY_NAME);
|
||||
if ("ACCEPT".equals(value)) {
|
||||
|
|
|
@ -15,13 +15,13 @@
|
|||
*/
|
||||
package ghidra.framework.main;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Font;
|
||||
import java.util.LinkedList;
|
||||
|
||||
import javax.swing.JTextPane;
|
||||
import javax.swing.text.*;
|
||||
|
||||
import docking.theme.GColor;
|
||||
import ghidra.framework.options.*;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.util.Msg;
|
||||
|
@ -113,7 +113,8 @@ public class ConsoleTextPane extends JTextPane implements OptionsChangeListener
|
|||
|
||||
private void updateFromOptions(Options options) {
|
||||
int newLimit = options.getInt(MAXIMUM_CHARACTERS_OPTION_NAME, DEFAULT_MAXIMUM_CHARS);
|
||||
truncationFactor = options.getDouble(TRUNCATION_FACTOR_OPTION_NAME, DEFAULT_TRUNCATION_FACTOR);
|
||||
truncationFactor =
|
||||
options.getDouble(TRUNCATION_FACTOR_OPTION_NAME, DEFAULT_TRUNCATION_FACTOR);
|
||||
setMaximumCharacterLimit(newLimit);
|
||||
}
|
||||
|
||||
|
@ -141,7 +142,7 @@ public class ConsoleTextPane extends JTextPane implements OptionsChangeListener
|
|||
|
||||
private void doAddMessage(MessageWrapper newMessage) {
|
||||
synchronized (messageList) {
|
||||
if ( !messageList.isEmpty() ) {
|
||||
if (!messageList.isEmpty()) {
|
||||
MessageWrapper lastMessage = messageList.getLast();
|
||||
if (lastMessage.merge(newMessage)) {
|
||||
return;
|
||||
|
@ -211,7 +212,8 @@ public class ConsoleTextPane extends JTextPane implements OptionsChangeListener
|
|||
outputAttributeSet.addAttribute(StyleConstants.FontSize, font.getSize());
|
||||
outputAttributeSet.addAttribute(StyleConstants.Italic, font.isItalic());
|
||||
outputAttributeSet.addAttribute(StyleConstants.Bold, font.isBold());
|
||||
outputAttributeSet.addAttribute(StyleConstants.Foreground, Color.BLACK);
|
||||
outputAttributeSet.addAttribute(StyleConstants.Foreground,
|
||||
new GColor("color.fg.consoletextpane"));
|
||||
|
||||
errorAttributeSet = new SimpleAttributeSet();
|
||||
errorAttributeSet.addAttribute(CUSTOM_ATTRIBUTE_KEY, ERROR_ATTRIBUTE_VALUE);
|
||||
|
@ -219,7 +221,8 @@ public class ConsoleTextPane extends JTextPane implements OptionsChangeListener
|
|||
errorAttributeSet.addAttribute(StyleConstants.FontSize, font.getSize());
|
||||
errorAttributeSet.addAttribute(StyleConstants.Italic, font.isItalic());
|
||||
errorAttributeSet.addAttribute(StyleConstants.Bold, font.isBold());
|
||||
errorAttributeSet.addAttribute(StyleConstants.Foreground, Color.RED);
|
||||
errorAttributeSet.addAttribute(StyleConstants.Foreground,
|
||||
new GColor("color.fg.error.consoletextpane"));
|
||||
}
|
||||
|
||||
private void doUpdate() {
|
||||
|
|
|
@ -25,6 +25,7 @@ import javax.swing.event.HyperlinkEvent;
|
|||
import javax.swing.text.View;
|
||||
|
||||
import docking.DockingUtils;
|
||||
import docking.theme.GColor;
|
||||
import docking.widgets.*;
|
||||
import docking.widgets.label.*;
|
||||
import generic.util.WindowUtilities;
|
||||
|
@ -58,7 +59,7 @@ class InfoPanel extends JPanel {
|
|||
|
||||
InfoPanel() {
|
||||
getAboutInfo();
|
||||
bgColor = new Color(243, 250, 255);
|
||||
bgColor = new GColor("color.bg.splash.infopanel");
|
||||
create();
|
||||
}
|
||||
|
||||
|
@ -174,7 +175,7 @@ class InfoPanel extends JPanel {
|
|||
Font font = versionLabel.getFont();
|
||||
font = font.deriveFont(14f).deriveFont(Font.BOLD);
|
||||
versionLabel.setFont(font);
|
||||
versionLabel.setForeground(Color.BLACK);
|
||||
versionLabel.setForeground(new GColor("color.fg.infopanel.version"));
|
||||
return versionLabel;
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
@ -16,25 +15,30 @@
|
|||
*/
|
||||
package ghidra.util.table;
|
||||
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.mem.Memory;
|
||||
import ghidra.program.model.symbol.ExternalLocation;
|
||||
import ghidra.program.model.symbol.Symbol;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Font;
|
||||
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.table.TableModel;
|
||||
|
||||
import docking.theme.GColor;
|
||||
import docking.widgets.table.GTableCellRenderer;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.mem.Memory;
|
||||
import ghidra.program.model.symbol.ExternalLocation;
|
||||
import ghidra.program.model.symbol.Symbol;
|
||||
|
||||
public class GhidraTableCellRenderer extends GTableCellRenderer {
|
||||
|
||||
// Defaults as defined by OptionsGui class - would be nice to use the tool options
|
||||
private static final Color BAD_REF_ADDR_COLOR = Color.red;
|
||||
private static final Color EXT_REF_RESOLVED_COLOR = Color.CYAN.darker().darker();
|
||||
private static final Color BAD_REF_ADDR_COLOR = new GColor("color.fg.listing.ref.bad");
|
||||
private static final Color EXT_REF_RESOLVED_COLOR =
|
||||
new GColor("color.fg.listing.ref.ext.resolved");
|
||||
public Color SELECTED_CELL_COLOR = new GColor("color.bg.table.selected.ghidratable");
|
||||
public Color BAD_EQUATE_COLOR = new GColor("color.fg.table.ghidratable.equate.bad");
|
||||
public Color EQUATE_COLOR = new GColor("color.fg.table.ghidratable.equate");
|
||||
public Color SUGGESTION_COLOR = new GColor("color.fg.table.ghidratable.suggestion");
|
||||
|
||||
public GhidraTableCellRenderer() {
|
||||
// default constructor
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
##MODULE IP: Oxygen Icons - LGPL 3.0
|
||||
Module.manifest||GHIDRA||||END|
|
||||
data/ExtensionPoint.manifest||GHIDRA||reviewed||END|
|
||||
data/byteviewer.theme.properties||GHIDRA||||END|
|
||||
src/main/help/help/TOC_Source.xml||GHIDRA||reviewed||END|
|
||||
src/main/help/help/shared/arrow.gif||GHIDRA||reviewed||END|
|
||||
src/main/help/help/shared/close16.gif||GHIDRA||reviewed||END|
|
||||
|
|
17
Ghidra/Features/ByteViewer/data/byteviewer.theme.properties
Normal file
17
Ghidra/Features/ByteViewer/data/byteviewer.theme.properties
Normal file
|
@ -0,0 +1,17 @@
|
|||
[Defaults]
|
||||
|
||||
color.bg.byteviewer = color.bg
|
||||
|
||||
color.fg.byteviewer.novalue = blue
|
||||
color.fg.byteviewer.changed = red
|
||||
color.cursor.focused.byteviewer.current = color.cursor.focused
|
||||
color.cursor.focused.byteviewer.noncurrent = black
|
||||
color.cursor.unfocused.byteviewer = color.cursor.unfocused
|
||||
|
||||
[Dark Defaults]
|
||||
color.fg.byteviewer.novalue = DarkBlue
|
||||
color.fg.byteviewer.changed = indianRed
|
||||
color.cursor.focused.byteviewer.current = color.cursor.focused
|
||||
color.cursor.focused.byteviewer.noncurrent = gray
|
||||
color.cursor.unfocused.byteviewer = color.cursor.unfocused
|
||||
|
|
@ -25,6 +25,7 @@ import java.util.List;
|
|||
import javax.swing.JComponent;
|
||||
|
||||
import docking.action.ToggleDockingAction;
|
||||
import docking.theme.GColor;
|
||||
import ghidra.GhidraOptions;
|
||||
import ghidra.GhidraOptions.CURSOR_MOUSE_BUTTON_NAMES;
|
||||
import ghidra.app.plugin.core.format.*;
|
||||
|
@ -55,11 +56,17 @@ public abstract class ByteViewerComponentProvider extends ComponentProviderAdapt
|
|||
|
||||
static final Font DEFAULT_FONT = new Font("Monospaced", Font.PLAIN, 12);
|
||||
static final int DEFAULT_BYTES_PER_LINE = 16;
|
||||
static final Color DEFAULT_MISSING_VALUE_COLOR = Color.blue;
|
||||
static final Color DEFAULT_EDIT_COLOR = Color.red;
|
||||
static final Color DEFAULT_CURRENT_CURSOR_COLOR = Color.magenta.brighter();
|
||||
static final Color DEFAULT_CURSOR_COLOR = Color.black;
|
||||
static final Color DEFAULT_NONFOCUS_CURSOR_COLOR = Color.darkGray;
|
||||
|
||||
//@formatter:off
|
||||
static final String FG = "byteviewer.color.fg";
|
||||
static final String CURSOR = "byteviewer.color.cursor";
|
||||
static final Color DEFAULT_MISSING_VALUE_COLOR = new GColor("color.fg.byteviewer.novalue");
|
||||
static final Color DEFAULT_EDIT_COLOR = new GColor("color.fg.byteviewer.changed");
|
||||
static final Color DEFAULT_CURRENT_CURSOR_COLOR = new GColor("color.cursor.focused.byteviewer.current");
|
||||
static final Color DEFAULT_CURSOR_COLOR = new GColor("color.cursor.focused.byteviewer.noncurrent");
|
||||
static final Color DEFAULT_NONFOCUS_CURSOR_COLOR = new GColor("color.cursor.unfocused.byteviewer");
|
||||
//@formatter:on
|
||||
|
||||
private static final Color DEFAULT_CURSOR_LINE_COLOR = GhidraOptions.DEFAULT_CURSOR_LINE_COLOR;
|
||||
|
||||
static final String DEFAULT_INDEX_NAME = "Addresses";
|
||||
|
@ -101,8 +108,7 @@ public abstract class ByteViewerComponentProvider extends ComponentProviderAdapt
|
|||
private Map<String, Class<? extends DataFormatModel>> dataFormatModelClassMap;
|
||||
|
||||
protected ByteViewerComponentProvider(PluginTool tool, AbstractByteViewerPlugin<?> plugin,
|
||||
String name,
|
||||
Class<?> contextType) {
|
||||
String name, Class<?> contextType) {
|
||||
super(tool, name, plugin.getName(), contextType);
|
||||
this.plugin = plugin;
|
||||
|
||||
|
@ -265,7 +271,8 @@ public abstract class ByteViewerComponentProvider extends ComponentProviderAdapt
|
|||
CURSOR_HIGHLIGHT_BUTTON_NAME, GhidraOptions.CURSOR_MOUSE_BUTTON_NAMES.MIDDLE);
|
||||
panel.setHighlightButton(mouseButton.getMouseEventID());
|
||||
|
||||
panel.setMouseButtonHighlightColor(opt.getColor(HIGHLIGHT_COLOR_NAME, Color.YELLOW));
|
||||
panel.setMouseButtonHighlightColor(
|
||||
opt.getColor(HIGHLIGHT_COLOR_NAME, DEFAULT_HIGHLIGHT_COLOR));
|
||||
|
||||
opt.addOptionsChangeListener(this);
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import java.util.List;
|
|||
import javax.swing.*;
|
||||
import javax.swing.event.*;
|
||||
|
||||
import docking.theme.GColor;
|
||||
import docking.widgets.fieldpanel.*;
|
||||
import docking.widgets.fieldpanel.field.EmptyTextField;
|
||||
import docking.widgets.fieldpanel.field.Field;
|
||||
|
@ -259,8 +260,8 @@ public class ByteViewerPanel extends JPanel
|
|||
String start = blocks[0].getLocationRepresentation(BigInteger.ZERO);
|
||||
startField.setText(start);
|
||||
ByteBlock lastBlock = blocks[blocks.length - 1];
|
||||
endField.setText(lastBlock.getLocationRepresentation(
|
||||
lastBlock.getLength().subtract(BigInteger.ONE)));
|
||||
endField.setText(lastBlock
|
||||
.getLocationRepresentation(lastBlock.getLength().subtract(BigInteger.ONE)));
|
||||
|
||||
indexPanelWidth = getIndexPanelWidth(blocks);
|
||||
int center = indexPanelWidth / 2;
|
||||
|
@ -833,7 +834,8 @@ public class ByteViewerPanel extends JPanel
|
|||
|
||||
columnHeader.addColumn(ByteViewerComponentProvider.DEFAULT_INDEX_NAME, indexPanel);
|
||||
scrollp.setColumnHeaderComp(columnHeader);
|
||||
compPanel.setBackground(Color.WHITE);
|
||||
|
||||
compPanel.setBackground(new GColor("color.bg.byteviewer"));
|
||||
|
||||
statusPanel = createStatusPanel();
|
||||
add(scrollp, BorderLayout.CENTER);
|
||||
|
|
|
@ -24,6 +24,7 @@ import java.awt.Font;
|
|||
import java.awt.event.MouseEvent;
|
||||
import java.io.IOException;
|
||||
|
||||
import docking.theme.GColor;
|
||||
import ghidra.GhidraOptions.CURSOR_MOUSE_BUTTON_NAMES;
|
||||
import ghidra.app.util.HelpTopics;
|
||||
import ghidra.framework.options.Options;
|
||||
|
@ -298,51 +299,52 @@ public class DecompileOptions {
|
|||
}
|
||||
}
|
||||
|
||||
//@formatter:off
|
||||
private final static IntegerFormatEnum INTEGERFORMAT_OPTIONDEFAULT = IntegerFormatEnum.BestFit; // Must match PrintLanguage::resetDefaultsInternal
|
||||
private IntegerFormatEnum integerFormat;
|
||||
|
||||
private final static Color HIGHLIGHT_MIDDLE_MOUSE_DEF = new Color(255, 255, 0, 128);
|
||||
private final static Color HIGHLIGHT_MIDDLE_MOUSE_DEF = new GColor("color.bg.decompiler.middle-mouse");
|
||||
private Color middleMouseHighlightColor;
|
||||
private int middleMouseHighlightButton = MouseEvent.BUTTON2;
|
||||
|
||||
private final static String HIGHLIGHT_CURRENT_VARIABLE_MSG =
|
||||
"Display.Color for Current Variable Highlight";
|
||||
private final static Color HIGHLIGHT_CURRENT_VARIABLE_DEF = new Color(255, 255, 0, 128);
|
||||
private final static String HIGHLIGHT_CURRENT_VARIABLE_MSG ="Display.Color for Current Variable Highlight";
|
||||
private final static Color HIGHLIGHT_CURRENT_VARIABLE_DEF = new GColor("color.bg.decompiler.current-variable");
|
||||
private Color currentVariableHighlightColor;
|
||||
|
||||
private final static String HIGHLIGHT_KEYWORD_MSG = "Display.Color for Keywords";
|
||||
private final static Color HIGHLIGHT_KEYWORD_DEF = Color.decode("0x0001E6");
|
||||
private final static Color HIGHLIGHT_KEYWORD_DEF = new GColor("color.fg.decompiler.keyword");
|
||||
private Color keywordColor;
|
||||
private final static String HIGHLIGHT_FUNCTION_MSG = "Display.Color for Function names";
|
||||
private final static Color HIGHLIGHT_FUNCTION_DEF = Color.decode("0x0000FF");
|
||||
private final static Color HIGHLIGHT_FUNCTION_DEF = new GColor("color.fg.decompiler.keyword");
|
||||
private Color functionColor;
|
||||
private final static String HIGHLIGHT_COMMENT_MSG = "Display.Color for Comments";
|
||||
private final static Color HIGHLIGHT_COMMENT_DEF = Color.decode("0x9600FF");
|
||||
private final static Color HIGHLIGHT_COMMENT_DEF = new GColor("color.fg.decompiler.comment");
|
||||
private Color commentColor;
|
||||
private final static String HIGHLIGHT_VARIABLE_MSG = "Display.Color for Variables";
|
||||
private final static Color HIGHLIGHT_VARIABLE_DEF = Color.decode("0x999900");
|
||||
private final static Color HIGHLIGHT_VARIABLE_DEF = new GColor( "color.fg.decompiler.variable");
|
||||
private Color variableColor;
|
||||
private final static String HIGHLIGHT_CONST_MSG = "Display.Color for Constants";
|
||||
private final static Color HIGHLIGHT_CONST_DEF = Color.decode("0x008E00");
|
||||
private final static Color HIGHLIGHT_CONST_DEF = new GColor( "color.fg.decompiler.constant");
|
||||
private Color constantColor;
|
||||
private final static String HIGHLIGHT_TYPE_MSG = "Display.Color for Types";
|
||||
private final static Color HIGHLIGHT_TYPE_DEF = Color.decode("0x0033CC");
|
||||
private final static Color HIGHLIGHT_TYPE_DEF = new GColor( "color.fg.decompiler.type");
|
||||
private Color typeColor;
|
||||
private final static String HIGHLIGHT_PARAMETER_MSG = "Display.Color for Parameters";
|
||||
private final static Color HIGHLIGHT_PARAMETER_DEF = Color.decode("0x9B009B");
|
||||
private final static Color HIGHLIGHT_PARAMETER_DEF = new GColor( "color.fg.decompiler.parameter");
|
||||
private Color parameterColor;
|
||||
private final static String HIGHLIGHT_GLOBAL_MSG = "Display.Color for Globals";
|
||||
private final static Color HIGHLIGHT_GLOBAL_DEF = Color.decode("0x009999");
|
||||
private final static Color HIGHLIGHT_GLOBAL_DEF = new GColor( "color.fg.decompiler.global");
|
||||
private Color globalColor;
|
||||
private final static String HIGHLIGHT_SPECIAL_MSG = "Display.Color for Special";
|
||||
private final static Color HIGHLIGHT_SPECIAL_DEF = Color.decode("0xCC0033");
|
||||
private Color specialColor;
|
||||
private final static String HIGHLIGHT_DEFAULT_MSG = "Display.Color Default";
|
||||
private final static Color HIGHLIGHT_DEFAULT_DEF = Color.BLACK;
|
||||
private final static Color HIGHLIGHT_DEFAULT_DEF = new GColor("color.fg.decompiler");
|
||||
private Color defaultColor;
|
||||
//@formatter:on
|
||||
|
||||
private static final String CODE_VIEWER_BACKGROUND_COLOR_MSG = "Display.Background Color";
|
||||
private static final Color CODE_VIEWER_BACKGROUND_COLOR = Color.WHITE;
|
||||
private static final Color CODE_VIEWER_BACKGROUND_COLOR = new GColor("color.bg.decompiler");
|
||||
private Color codeViewerBackgroundColor;
|
||||
|
||||
private static final String SEARCH_HIGHLIGHT_MSG =
|
||||
|
|
|
@ -21,6 +21,7 @@ import java.util.Map.Entry;
|
|||
|
||||
import org.jdom.Element;
|
||||
|
||||
import docking.theme.GColor;
|
||||
import edu.uci.ics.jung.algorithms.layout.Layout;
|
||||
import edu.uci.ics.jung.visualization.RenderContext;
|
||||
import edu.uci.ics.jung.visualization.picking.PickedState;
|
||||
|
@ -45,9 +46,12 @@ import ghidra.util.UndefinedFunction;
|
|||
|
||||
public class FGComponent extends GraphComponent<FGVertex, FGEdge, FunctionGraph> {
|
||||
|
||||
private static final Color END_COLOR = new Color(255, 127, 127);
|
||||
private static final Color START_COLOR = new Color(127, 255, 127);
|
||||
private static final Color UNDEFINED_FUNCTION_COLOR = new Color(220, 220, 220);
|
||||
//@formatter:off
|
||||
private static final Color PICKED_COLOR = new GColor("color.bg.functiongraph.vertex.picked");
|
||||
private static final Color START_COLOR = new GColor("color.bg.functiongraph.vertex.entry");
|
||||
private static final Color END_COLOR = new GColor("color.bg.functiongraph.vertex.exit");
|
||||
private static final Color UNDEFINED_FUNCTION_COLOR = new GColor("color.bg.undefined");
|
||||
//@formatter:on
|
||||
|
||||
/**
|
||||
* A somewhat arbitrary value that is used to signal a 'big' graph, which is one that will
|
||||
|
@ -206,7 +210,7 @@ public class FGComponent extends GraphComponent<FGVertex, FGEdge, FunctionGraph>
|
|||
// for background colors when we are zoomed to far to render the listing
|
||||
PickedState<FGVertex> pickedVertexState = viewer.getPickedVertexState();
|
||||
renderContext.setVertexFillPaintTransformer(new FGVertexPickableBackgroundPaintTransformer(
|
||||
pickedVertexState, Color.YELLOW, START_COLOR, END_COLOR));
|
||||
pickedVertexState, PICKED_COLOR, START_COLOR, END_COLOR));
|
||||
|
||||
// edge label rendering
|
||||
com.google.common.base.Function<FGEdge, String> edgeLabelTransformer = e -> e.getLabel();
|
||||
|
@ -233,7 +237,7 @@ public class FGComponent extends GraphComponent<FGVertex, FGEdge, FunctionGraph>
|
|||
viewer.setBackground(UNDEFINED_FUNCTION_COLOR);
|
||||
}
|
||||
else {
|
||||
viewer.setBackground(Color.WHITE);
|
||||
viewer.setBackground(new GColor("color.bg.functiongraph"));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -257,7 +261,7 @@ public class FGComponent extends GraphComponent<FGVertex, FGEdge, FunctionGraph>
|
|||
|
||||
PickedState<FGVertex> pickedVertexState = viewer.getPickedVertexState();
|
||||
renderContext.setVertexFillPaintTransformer(new FGVertexPickableBackgroundPaintTransformer(
|
||||
pickedVertexState, Color.YELLOW, START_COLOR, END_COLOR));
|
||||
pickedVertexState, PICKED_COLOR, START_COLOR, END_COLOR));
|
||||
|
||||
viewer.setGraphOptions(vgOptions);
|
||||
|
||||
|
|
|
@ -17,9 +17,11 @@ package ghidra.app.plugin.core.functiongraph.graph.jung.transformer;
|
|||
|
||||
import java.awt.Color;
|
||||
import java.awt.Paint;
|
||||
import java.util.Objects;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
|
||||
import docking.theme.Gui;
|
||||
import edu.uci.ics.jung.visualization.picking.PickedInfo;
|
||||
import ghidra.app.plugin.core.functiongraph.graph.FGVertexType;
|
||||
import ghidra.app.plugin.core.functiongraph.graph.vertex.FGVertex;
|
||||
|
@ -31,8 +33,8 @@ public class FGVertexPickableBackgroundPaintTransformer implements Function<FGVe
|
|||
private final Color pickedColor;
|
||||
private final Color entryColor;
|
||||
private final Color exitColor;
|
||||
private final Color pickedStartColor;
|
||||
private final Color pickedEndColor;
|
||||
private final Color pickedEntryColor;
|
||||
private final Color pickedExitColor;
|
||||
|
||||
private static Color mix(Color c1, Color c2) {
|
||||
return new Color((c1.getRed() + c2.getRed()) / 2, (c1.getGreen() + c2.getGreen()) / 2,
|
||||
|
@ -42,15 +44,12 @@ public class FGVertexPickableBackgroundPaintTransformer implements Function<FGVe
|
|||
public FGVertexPickableBackgroundPaintTransformer(PickedInfo<FGVertex> info, Color pickedColor,
|
||||
Color startColor, Color endColor) {
|
||||
|
||||
if (info == null) {
|
||||
throw new IllegalArgumentException("PickedInfo instance must be non-null");
|
||||
}
|
||||
this.info = info;
|
||||
this.info = Objects.requireNonNull(info);
|
||||
this.pickedColor = pickedColor;
|
||||
this.entryColor = startColor;
|
||||
this.exitColor = endColor;
|
||||
this.pickedStartColor = mix(pickedColor, startColor);
|
||||
this.pickedEndColor = mix(pickedColor, endColor);
|
||||
this.pickedEntryColor = mix(pickedColor, startColor);
|
||||
this.pickedExitColor = mix(pickedColor, endColor);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -69,20 +68,31 @@ public class FGVertexPickableBackgroundPaintTransformer implements Function<FGVe
|
|||
if (info.isPicked(v)) {
|
||||
if (v.isDefaultBackgroundColor()) {
|
||||
if (vertexType.isEntry()) {
|
||||
return pickedStartColor;
|
||||
return pickedEntryColor;
|
||||
}
|
||||
if (vertexType.isExit()) {
|
||||
return pickedEndColor;
|
||||
return pickedExitColor;
|
||||
}
|
||||
return pickedColor;
|
||||
}
|
||||
if (vertexType.isEntry()) {
|
||||
return pickedStartColor.darker();
|
||||
// this is a vertex that has a non-default, user-defined color; making the value
|
||||
// darker() is meant to signal that the 'picked entry color' is on top of a vertex
|
||||
// that has another color underneath
|
||||
return Gui.darker(pickedEntryColor);
|
||||
}
|
||||
if (vertexType.isExit()) {
|
||||
return pickedEndColor.darker();
|
||||
// this is a vertex that has a non-default, user-defined color; making the value
|
||||
// darker() is meant to signal that the 'picked exit color' is on top of a vertex
|
||||
// that has another color underneath
|
||||
return Gui.darker(pickedExitColor);
|
||||
}
|
||||
return pickedColor.darker();
|
||||
|
||||
// this is a vertex that has a non-default, user-defined color; making the value
|
||||
// darker() is meant to signal that the 'picked color' is on top of a vertex that has
|
||||
// another color underneath
|
||||
Color mixed = mix(pickedColor, backgroundColor);
|
||||
return Gui.darker(mixed);
|
||||
}
|
||||
|
||||
if (vertexType.isEntry()) {
|
||||
|
|
|
@ -21,6 +21,7 @@ import java.awt.geom.Point2D;
|
|||
|
||||
import javax.swing.*;
|
||||
|
||||
import docking.theme.GThemeDefaults.Colors;
|
||||
import edu.uci.ics.jung.visualization.VisualizationViewer;
|
||||
import ghidra.app.plugin.core.functiongraph.graph.FGEdge;
|
||||
import ghidra.app.plugin.core.functiongraph.mvc.FGController;
|
||||
|
@ -133,7 +134,7 @@ public abstract class AbstractGraphComponentPanel extends JPanel {
|
|||
abstract ProgramLocation getProgramLocation();
|
||||
|
||||
boolean isDefaultBackgroundColor() {
|
||||
return getBackgroundColor().equals(Color.WHITE);
|
||||
return getBackgroundColor().equals(Colors.BACKGROUND);
|
||||
}
|
||||
|
||||
boolean isHeaderClick(Component clickedComponent) {
|
||||
|
|
|
@ -31,6 +31,7 @@ import docking.ActionContext;
|
|||
import docking.GenericHeader;
|
||||
import docking.action.DockingAction;
|
||||
import docking.action.ToolBarData;
|
||||
import docking.theme.GThemeDefaults.Colors;
|
||||
import docking.widgets.fieldpanel.FieldPanel;
|
||||
import docking.widgets.fieldpanel.Layout;
|
||||
import docking.widgets.fieldpanel.field.Field;
|
||||
|
@ -204,7 +205,6 @@ public class ListingGraphComponentPanel extends AbstractGraphComponentPanel {
|
|||
previewListingPanel = new FGVertexListingPanel(controller,
|
||||
getFormatManager(useFullSizeTooltip), program, addressSet);
|
||||
previewListingPanel.setTextBackgroundColor(FGVertex.TOOLTIP_BACKGROUND_COLOR);
|
||||
// previewListingPanel.getFieldPanel().setSelectionMode( FieldPanel.NO_SELECTION );
|
||||
previewListingPanel.getFieldPanel().setCursorOn(false);
|
||||
|
||||
// keep the tooltip window from getting too big; use an arbitrary, reasonable max
|
||||
|
@ -224,7 +224,7 @@ public class ListingGraphComponentPanel extends AbstractGraphComponentPanel {
|
|||
|
||||
JPanel headerPanel = new JPanel(new BorderLayout());
|
||||
headerPanel.add(tooltipTitleLabel);
|
||||
headerPanel.setBorder(BorderFactory.createLineBorder(Color.BLACK));
|
||||
headerPanel.setBorder(BorderFactory.createLineBorder(Colors.Java.BORDER));
|
||||
|
||||
panel.add(headerPanel, BorderLayout.NORTH);
|
||||
panel.add(previewListingPanel, BorderLayout.CENTER);
|
||||
|
|
|
@ -19,6 +19,7 @@ import java.awt.Color;
|
|||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import docking.theme.GColor;
|
||||
import ghidra.app.plugin.core.functiongraph.FunctionGraphPlugin;
|
||||
import ghidra.app.plugin.core.functiongraph.graph.layout.FGLayoutOptions;
|
||||
import ghidra.framework.options.Options;
|
||||
|
@ -85,24 +86,21 @@ public class FunctionGraphOptions extends VisualGraphOptions {
|
|||
"Signals that any user color changes to a group vertex will apply that same color to " +
|
||||
"all grouped vertices as well.";
|
||||
|
||||
public static final Color DEFAULT_VERTEX_BACKGROUND_COLOR = Color.WHITE;
|
||||
public static final Color DEFAULT_GROUP_BACKGROUND_COLOR = new Color(226, 255, 155);
|
||||
private static final Color HOVER_HIGHLIGHT_FALL_THROUGH_COLOR = new Color(255, 127, 127);
|
||||
private static final Color HOVER_HIGHLIGHT_UNCONDITIONAL_COLOR = new Color(127, 127, 255);
|
||||
private static final Color HOVER_HIGHLIGHT_CONDITIONAL_COLOR = Color.GREEN;
|
||||
|
||||
private Color defaultVertexBackgroundColor = DEFAULT_VERTEX_BACKGROUND_COLOR;
|
||||
|
||||
private boolean updateGroupColorsAutomatically = true;
|
||||
private Color defaultGroupBackgroundColor = DEFAULT_GROUP_BACKGROUND_COLOR;
|
||||
|
||||
private Color fallthroughEdgeColor = Color.RED;
|
||||
private Color unconditionalJumpEdgeColor = Color.BLUE;
|
||||
private Color conditionalJumpEdgeColor = Color.GREEN.darker().darker();
|
||||
//@formatter:off
|
||||
public static final Color DEFAULT_GROUP_BACKGROUND_COLOR = new GColor("color.bg.functiongraph.vertex.group");
|
||||
private Color defaultVertexBackgroundColor = new GColor("color.bg.functiongraph");
|
||||
private Color defaultGroupBackgroundColor = new GColor("color.bg.functiongraph.vertex.group");
|
||||
|
||||
private Color fallthroughEdgeHighlightColor = HOVER_HIGHLIGHT_FALL_THROUGH_COLOR;
|
||||
private Color unconditionalJumpEdgeHighlightColor = HOVER_HIGHLIGHT_UNCONDITIONAL_COLOR;
|
||||
private Color conditionalJumpEdgeHighlightColor = HOVER_HIGHLIGHT_CONDITIONAL_COLOR;
|
||||
private Color fallthroughEdgeColor = new GColor("color.bg.functiongraph.edge.fall-through");
|
||||
private Color conditionalJumpEdgeColor = new GColor("color.bg.functiongraph.edge.jump.conditional");
|
||||
private Color unconditionalJumpEdgeColor = new GColor("color.bg.functiongraph.edge.jump.unconditional");
|
||||
|
||||
private Color fallthroughEdgeHighlightColor = new GColor("color.bg.functiongraph.edge.fall-through.highlight");
|
||||
private Color conditionalJumpEdgeHighlightColor = new GColor("color.bg.functiongraph.edge.jump.conditional.highlight");
|
||||
private Color unconditionalJumpEdgeHighlightColor = new GColor("color.bg.functiongraph.edge.jump.unconditional.highlight");
|
||||
//@formatter:on
|
||||
|
||||
private boolean useFullSizeTooltip = false;
|
||||
|
||||
|
@ -174,10 +172,10 @@ public class FunctionGraphOptions extends VisualGraphOptions {
|
|||
options.registerOption(USE_CONDENSED_LAYOUT_KEY, useCondensedLayout(),
|
||||
new HelpLocation(OWNER, "Layout_Compressing"), USE_CONDENSED_LAYOUT_DESCRIPTION);
|
||||
|
||||
options.registerOption(DEFAULT_VERTEX_BACKGROUND_COLOR_KEY, DEFAULT_VERTEX_BACKGROUND_COLOR,
|
||||
options.registerOption(DEFAULT_VERTEX_BACKGROUND_COLOR_KEY, defaultVertexBackgroundColor,
|
||||
help, DEFAULT_VERTEX_BACKGROUND_COLOR_DESCRPTION);
|
||||
|
||||
options.registerOption(DEFAULT_GROUP_BACKGROUND_COLOR_KEY, DEFAULT_GROUP_BACKGROUND_COLOR,
|
||||
options.registerOption(DEFAULT_GROUP_BACKGROUND_COLOR_KEY, defaultGroupBackgroundColor,
|
||||
help, DEFAULT_GROUP_BACKGROUND_COLOR_DESCRPTION);
|
||||
|
||||
options.registerOption(UPDATE_GROUP_AND_UNGROUP_COLORS, updateGroupColorsAutomatically,
|
||||
|
@ -238,10 +236,10 @@ public class FunctionGraphOptions extends VisualGraphOptions {
|
|||
useFullSizeTooltip = options.getBoolean(USE_FULL_SIZE_TOOLTIP_KEY, useFullSizeTooltip);
|
||||
|
||||
defaultVertexBackgroundColor =
|
||||
options.getColor(DEFAULT_VERTEX_BACKGROUND_COLOR_KEY, DEFAULT_VERTEX_BACKGROUND_COLOR);
|
||||
options.getColor(DEFAULT_VERTEX_BACKGROUND_COLOR_KEY, defaultVertexBackgroundColor);
|
||||
|
||||
defaultGroupBackgroundColor =
|
||||
options.getColor(DEFAULT_GROUP_BACKGROUND_COLOR_KEY, DEFAULT_GROUP_BACKGROUND_COLOR);
|
||||
options.getColor(DEFAULT_GROUP_BACKGROUND_COLOR_KEY, defaultGroupBackgroundColor);
|
||||
|
||||
updateGroupColorsAutomatically =
|
||||
options.getBoolean(UPDATE_GROUP_AND_UNGROUP_COLORS, updateGroupColorsAutomatically);
|
||||
|
|
|
@ -23,6 +23,7 @@ import java.util.List;
|
|||
import javax.swing.*;
|
||||
|
||||
import docking.DialogComponentProvider;
|
||||
import docking.theme.GThemeDefaults;
|
||||
import docking.widgets.checkbox.GCheckBox;
|
||||
import ghidra.feature.fid.db.FidFile;
|
||||
import ghidra.util.HelpLocation;
|
||||
|
@ -89,7 +90,7 @@ public class ActiveFidConfigureDialog extends DialogComponentProvider {
|
|||
private Component buildCheckBoxPanel() {
|
||||
JPanel panel = new JPanel(new VerticalLayout(5));
|
||||
panel.setOpaque(true);
|
||||
panel.setBackground(Color.WHITE);
|
||||
panel.setBackground(GThemeDefaults.Colors.BACKGROUND);
|
||||
panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
|
||||
for (FidFile fidFile : fidFiles) {
|
||||
GCheckBox checkbox = new GCheckBox(fidFile.getName(), fidFile.isActive());
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
##VERSION: 2.0
|
||||
##MODULE IP: Oxygen Icons - LGPL 3.0
|
||||
Module.manifest||GHIDRA||||END|
|
||||
data/functioncall.theme.properties||GHIDRA||||END|
|
||||
src/main/help/help/TOC_Source.xml||GHIDRA||||END|
|
||||
src/main/help/help/shared/arrow.gif||GHIDRA||||END|
|
||||
src/main/help/help/shared/close16.gif||GHIDRA||||END|
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
[Defaults]
|
||||
|
||||
color.fcg.satellite.edge = rgba(0,0,0,0.1)
|
|
@ -17,6 +17,7 @@ package functioncalls.graph.view;
|
|||
|
||||
import java.awt.Color;
|
||||
|
||||
import docking.theme.GColor;
|
||||
import edu.uci.ics.jung.visualization.RenderContext;
|
||||
import functioncalls.graph.*;
|
||||
import functioncalls.graph.renderer.FcgEdgePaintTransformer;
|
||||
|
@ -41,7 +42,7 @@ public class FcgComponent extends GraphComponent<FcgVertex, FcgEdge, FunctionCal
|
|||
private Color lightGray = new Color(233, 233, 233);
|
||||
|
||||
// the satellite gets too cluttered, so wash out the edges
|
||||
private Color washedOutBlack = new Color(0, 0, 0, 25);
|
||||
private Color washedOutBlack = new GColor("color.fcg.satellite.edge");
|
||||
|
||||
private FcgEdgePaintTransformer edgePaintTransformer =
|
||||
new FcgEdgePaintTransformer(lightGreen, lightGray);
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
##MODULE IP: Tango Icons - Public Domain
|
||||
Module.manifest||GHIDRA||||END|
|
||||
data/ExtensionPoint.manifest||GHIDRA||||END|
|
||||
data/graphservices.theme.properties||GHIDRA||||END|
|
||||
src/main/help/help/TOC_Source.xml||GHIDRA||||END|
|
||||
src/main/help/help/shared/arrow.gif||GHIDRA||||END|
|
||||
src/main/help/help/shared/note.png||Oxygen Icons - LGPL 3.0|||Oxygen icon theme (dual license; LGPL or CC-SA-3.0)|END|
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
[Defaults]
|
||||
|
||||
color.bg.graph = color.bg
|
|
@ -50,6 +50,7 @@ import docking.action.builder.*;
|
|||
import docking.menu.ActionState;
|
||||
import docking.menu.MultiStateDockingAction;
|
||||
import docking.options.editor.OptionsDialog;
|
||||
import docking.theme.GColor;
|
||||
import docking.widgets.EventTrigger;
|
||||
import docking.widgets.OptionDialog;
|
||||
import generic.util.WindowUtilities;
|
||||
|
@ -78,6 +79,8 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
|||
private static final Dimension PREFERRED_VIEW_SIZE = new Dimension(1000, 1000);
|
||||
private static final Dimension PREFERRED_LAYOUT_SIZE = new Dimension(3000, 3000);
|
||||
|
||||
private static Color BACKGROUND_COLOR = new GColor("color.bg.graph");
|
||||
|
||||
// layout algorithm categories
|
||||
static final String MIN_CROSS = "Hierarchical MinCross";
|
||||
static final String VERT_MIN_CROSS = "Vertical Hierarchical MinCross";
|
||||
|
@ -187,8 +190,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
|||
if (graphDisplayProvider.getDefaultSatelliteState()) {
|
||||
viewer.getComponent().add(satelliteViewer.getComponent());
|
||||
}
|
||||
layoutTransitionManager =
|
||||
new LayoutTransitionManager(viewer, this::isRoot, graphRenderer);
|
||||
layoutTransitionManager = new LayoutTransitionManager(viewer, this::isRoot, graphRenderer);
|
||||
|
||||
viewer.getComponent().addComponentListener(new ComponentAdapter() {
|
||||
@Override
|
||||
|
@ -203,9 +205,9 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
|||
}
|
||||
});
|
||||
|
||||
viewer.setInitialDimensionFunction(InitialDimensionFunction
|
||||
.builder(viewer.getRenderContext().getVertexBoundsFunction())
|
||||
.build());
|
||||
viewer.setInitialDimensionFunction(
|
||||
InitialDimensionFunction.builder(viewer.getRenderContext().getVertexBoundsFunction())
|
||||
.build());
|
||||
createToolbarActions();
|
||||
createPopupActions();
|
||||
connectSelectionStateListeners();
|
||||
|
@ -260,9 +262,8 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
|||
}
|
||||
};
|
||||
|
||||
MutableTransformer transformer = viewer.getRenderContext()
|
||||
.getMultiLayerTransformer()
|
||||
.getTransformer(VIEW);
|
||||
MutableTransformer transformer =
|
||||
viewer.getRenderContext().getMultiLayerTransformer().getTransformer(VIEW);
|
||||
|
||||
MagnifyShapeTransformer shapeTransformer = MagnifyShapeTransformer.builder(lens)
|
||||
// this lens' delegate is the viewer's VIEW layer, abandoned above
|
||||
|
@ -283,23 +284,21 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
|||
viewer.removePostRenderPaintable(singleSelectedVertexPaintable);
|
||||
|
||||
// for highlighting of multiple selected vertices
|
||||
this.multiSelectedVertexPaintable =
|
||||
MultiSelectedVertexPaintable.builder(viewer)
|
||||
.selectionStrokeMin(15.f)
|
||||
.selectionPaint(getSelectedVertexColor())
|
||||
.useBounds(true)
|
||||
.useOval(true)
|
||||
.highlightScale(1.15)
|
||||
.fillHighlight(false)
|
||||
.build();
|
||||
this.multiSelectedVertexPaintable = MultiSelectedVertexPaintable.builder(viewer)
|
||||
.selectionStrokeMin(15.f)
|
||||
.selectionPaint(getSelectedVertexColor())
|
||||
.useBounds(true)
|
||||
.useOval(true)
|
||||
.highlightScale(1.15)
|
||||
.fillHighlight(false)
|
||||
.build();
|
||||
|
||||
// manages highlight painting of a single selected vertex
|
||||
this.singleSelectedVertexPaintable =
|
||||
SingleSelectedVertexPaintable.builder(viewer)
|
||||
.selectionStrokeMin(4.f)
|
||||
.selectionPaint(getSelectedVertexColor())
|
||||
.selectedVertexFunction(vs -> this.focusedVertex)
|
||||
.build();
|
||||
this.singleSelectedVertexPaintable = SingleSelectedVertexPaintable.builder(viewer)
|
||||
.selectionStrokeMin(4.f)
|
||||
.selectionPaint(getSelectedVertexColor())
|
||||
.selectedVertexFunction(vs -> this.focusedVertex)
|
||||
.build();
|
||||
|
||||
// draws the selection highlights
|
||||
viewer.addPreRenderPaintable(multiSelectedVertexPaintable);
|
||||
|
@ -352,8 +351,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
|||
.buildAndInstallLocal(componentProvider);
|
||||
|
||||
// create an icon button to reset the view transformations to identity (scaled to layout)
|
||||
new ActionBuilder("Reset View", ACTION_OWNER)
|
||||
.description("Fit Graph to Window")
|
||||
new ActionBuilder("Reset View", ACTION_OWNER).description("Fit Graph to Window")
|
||||
.toolBarIcon(DefaultDisplayGraphIcons.FIT_TO_WINDOW)
|
||||
.onAction(context -> centerAndScale())
|
||||
.buildAndInstallLocal(componentProvider);
|
||||
|
@ -363,8 +361,8 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
|||
ToggleDockingAction lensToggle = new ToggleActionBuilder("View Magnifier", ACTION_OWNER)
|
||||
.description("Show View Magnifier")
|
||||
.toolBarIcon(DefaultDisplayGraphIcons.VIEW_MAGNIFIER_ICON)
|
||||
.onAction(context -> magnifyViewSupport.activate(
|
||||
((AbstractButton) context.getSourceObject()).isSelected()))
|
||||
.onAction(context -> magnifyViewSupport
|
||||
.activate(((AbstractButton) context.getSourceObject()).isSelected()))
|
||||
.build();
|
||||
magnifyViewSupport.addItemListener(
|
||||
itemEvent -> lensToggle.setSelected(itemEvent.getStateChange() == ItemEvent.SELECTED));
|
||||
|
@ -388,40 +386,35 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
|||
}
|
||||
|
||||
private void createPopupActions() {
|
||||
new ActionBuilder("Select Vertex", ACTION_OWNER)
|
||||
.popupMenuPath("Select Vertex")
|
||||
new ActionBuilder("Select Vertex", ACTION_OWNER).popupMenuPath("Select Vertex")
|
||||
.popupMenuGroup("selection", "1")
|
||||
.withContext(VertexGraphActionContext.class)
|
||||
.enabledWhen(c -> !isSelected(c.getClickedVertex()))
|
||||
.onAction(c -> viewer.getSelectedVertexState().select(c.getClickedVertex()))
|
||||
.buildAndInstallLocal(componentProvider);
|
||||
|
||||
new ActionBuilder("Deselect Vertex", ACTION_OWNER)
|
||||
.popupMenuPath("Deselect Vertex")
|
||||
new ActionBuilder("Deselect Vertex", ACTION_OWNER).popupMenuPath("Deselect Vertex")
|
||||
.popupMenuGroup("selection", "2")
|
||||
.withContext(VertexGraphActionContext.class)
|
||||
.enabledWhen(c -> isSelected(c.getClickedVertex()))
|
||||
.onAction(c -> viewer.getSelectedVertexState().deselect(c.getClickedVertex()))
|
||||
.buildAndInstallLocal(componentProvider);
|
||||
|
||||
new ActionBuilder("Select Edge", ACTION_OWNER)
|
||||
.popupMenuPath("Select Edge")
|
||||
new ActionBuilder("Select Edge", ACTION_OWNER).popupMenuPath("Select Edge")
|
||||
.popupMenuGroup("selection", "1")
|
||||
.withContext(EdgeGraphActionContext.class)
|
||||
.enabledWhen(c -> !isSelected(c.getClickedEdge()))
|
||||
.onAction(c -> selectEdge(c.getClickedEdge()))
|
||||
.buildAndInstallLocal(componentProvider);
|
||||
|
||||
new ActionBuilder("Deselect Edge", ACTION_OWNER)
|
||||
.popupMenuPath("Deselect Edge")
|
||||
new ActionBuilder("Deselect Edge", ACTION_OWNER).popupMenuPath("Deselect Edge")
|
||||
.popupMenuGroup("selection", "2")
|
||||
.withContext(EdgeGraphActionContext.class)
|
||||
.enabledWhen(c -> isSelected(c.getClickedEdge()))
|
||||
.onAction(c -> deselectEdge(c.getClickedEdge()))
|
||||
.buildAndInstallLocal(componentProvider);
|
||||
|
||||
new ActionBuilder("Edge Source", ACTION_OWNER)
|
||||
.popupMenuPath("Go To Edge Source")
|
||||
new ActionBuilder("Edge Source", ACTION_OWNER).popupMenuPath("Go To Edge Source")
|
||||
.popupMenuGroup("Go To")
|
||||
.withContext(EdgeGraphActionContext.class)
|
||||
.onAction(c -> {
|
||||
|
@ -430,8 +423,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
|||
})
|
||||
.buildAndInstallLocal(componentProvider);
|
||||
|
||||
new ActionBuilder("Edge Target", ACTION_OWNER)
|
||||
.popupMenuPath("Go To Edge Target")
|
||||
new ActionBuilder("Edge Target", ACTION_OWNER).popupMenuPath("Go To Edge Target")
|
||||
.popupMenuGroup("Go To")
|
||||
.withContext(EdgeGraphActionContext.class)
|
||||
.onAction(c -> {
|
||||
|
@ -440,12 +432,12 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
|||
})
|
||||
.buildAndInstallLocal(componentProvider);
|
||||
|
||||
hideSelectedAction = new ToggleActionBuilder("Hide Selected", ACTION_OWNER)
|
||||
.popupMenuPath("Hide Selected")
|
||||
.popupMenuGroup("z", "1")
|
||||
.description("Toggles whether or not to show selected vertices and edges")
|
||||
.onAction(c -> manageVertexDisplay())
|
||||
.buildAndInstallLocal(componentProvider);
|
||||
hideSelectedAction =
|
||||
new ToggleActionBuilder("Hide Selected", ACTION_OWNER).popupMenuPath("Hide Selected")
|
||||
.popupMenuGroup("z", "1")
|
||||
.description("Toggles whether or not to show selected vertices and edges")
|
||||
.onAction(c -> manageVertexDisplay())
|
||||
.buildAndInstallLocal(componentProvider);
|
||||
|
||||
hideUnselectedAction = new ToggleActionBuilder("Hide Unselected", ACTION_OWNER)
|
||||
.popupMenuPath("Hide Unselected")
|
||||
|
@ -454,8 +446,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
|||
.onAction(c -> manageVertexDisplay())
|
||||
.buildAndInstallLocal(componentProvider);
|
||||
|
||||
new ActionBuilder("Invert Selection", ACTION_OWNER)
|
||||
.popupMenuPath("Invert Selection")
|
||||
new ActionBuilder("Invert Selection", ACTION_OWNER).popupMenuPath("Invert Selection")
|
||||
.popupMenuGroup("z", "3")
|
||||
.description("Inverts the current selection")
|
||||
.onAction(c -> invertSelection())
|
||||
|
@ -493,8 +484,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
|||
.onAction(c -> growSelection(getAllComponentVerticesFromSelected()))
|
||||
.buildAndInstallLocal(componentProvider);
|
||||
|
||||
new ActionBuilder("Clear Selection", ACTION_OWNER)
|
||||
.popupMenuPath("Clear Selection")
|
||||
new ActionBuilder("Clear Selection", ACTION_OWNER).popupMenuPath("Clear Selection")
|
||||
.popupMenuGroup("z", "5")
|
||||
.keyBinding("escape")
|
||||
.enabledWhen(c -> hasSelection())
|
||||
|
@ -516,8 +506,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
|||
.onAction(c -> groupSelectedVertices())
|
||||
.buildAndInstallLocal(componentProvider);
|
||||
|
||||
new ActionBuilder("Expand Selected", ACTION_OWNER)
|
||||
.popupMenuPath("Expand Selected Vertices")
|
||||
new ActionBuilder("Expand Selected", ACTION_OWNER).popupMenuPath("Expand Selected Vertices")
|
||||
.popupMenuGroup("zz", "6")
|
||||
.description("Expands all selected collapsed vertices into their previous form")
|
||||
.onAction(c -> ungroupSelectedVertices())
|
||||
|
@ -579,8 +568,8 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
|||
|
||||
private void askToNameGroupVertex(AttributedVertex vertex) {
|
||||
String name = vertex.getName();
|
||||
String userName = OptionDialog.showInputMultilineDialog(null, "Enter Group Vertex Text",
|
||||
"Text", name);
|
||||
String userName =
|
||||
OptionDialog.showInputMultilineDialog(null, "Enter Group Vertex Text", "Text", name);
|
||||
|
||||
updateVertexName(vertex, userName != null ? userName : name);
|
||||
}
|
||||
|
@ -602,8 +591,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
|||
}
|
||||
|
||||
private boolean hasSelection() {
|
||||
return !(viewer.getSelectedVertices().isEmpty() &&
|
||||
viewer.getSelectedEdges().isEmpty());
|
||||
return !(viewer.getSelectedVertices().isEmpty() && viewer.getSelectedEdges().isEmpty());
|
||||
}
|
||||
|
||||
private boolean isSelected(AttributedVertex v) {
|
||||
|
@ -648,15 +636,11 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
|||
|
||||
// select all the edges that connect the supplied vertices
|
||||
private void selectEdgesConnecting(Collection<AttributedVertex> vertices) {
|
||||
Set<AttributedEdge> edges = graph.edgeSet()
|
||||
.stream()
|
||||
.filter(
|
||||
e -> {
|
||||
AttributedVertex source = graph.getEdgeSource(e);
|
||||
AttributedVertex target = graph.getEdgeTarget(e);
|
||||
return vertices.contains(source) && vertices.contains(target);
|
||||
})
|
||||
.collect(Collectors.toSet());
|
||||
Set<AttributedEdge> edges = graph.edgeSet().stream().filter(e -> {
|
||||
AttributedVertex source = graph.getEdgeSource(e);
|
||||
AttributedVertex target = graph.getEdgeTarget(e);
|
||||
return vertices.contains(source) && vertices.contains(target);
|
||||
}).collect(Collectors.toSet());
|
||||
viewer.getSelectedEdgeState().select(edges);
|
||||
}
|
||||
|
||||
|
@ -813,12 +797,9 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
|||
private SatelliteVisualizationViewer<AttributedVertex, AttributedEdge> createSatelliteViewer(
|
||||
VisualizationViewer<AttributedVertex, AttributedEdge> parentViewer) {
|
||||
Dimension viewerSize = parentViewer.getSize();
|
||||
Dimension satelliteSize = new Dimension(
|
||||
viewerSize.width / 4, viewerSize.height / 4);
|
||||
Dimension satelliteSize = new Dimension(viewerSize.width / 4, viewerSize.height / 4);
|
||||
final SatelliteVisualizationViewer<AttributedVertex, AttributedEdge> satellite =
|
||||
SatelliteVisualizationViewer.builder(parentViewer)
|
||||
.viewSize(satelliteSize)
|
||||
.build();
|
||||
SatelliteVisualizationViewer.builder(parentViewer).viewSize(satelliteSize).build();
|
||||
|
||||
//
|
||||
// JUNGRAPHT CHANGE 3
|
||||
|
@ -1124,10 +1105,8 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
|||
componentProvider.setTitle(title);
|
||||
int count = graph.getVertexCount();
|
||||
if (count > options.getMaxNodeCount()) {
|
||||
Msg.showWarn(this, null, "Graph Not Rendered - Too many nodes!",
|
||||
"Exceeded limit of " + options.getMaxNodeCount() + " nodes.\n\n Graph contained " +
|
||||
count +
|
||||
" nodes!");
|
||||
Msg.showWarn(this, null, "Graph Not Rendered - Too many nodes!", "Exceeded limit of " +
|
||||
options.getMaxNodeCount() + " nodes.\n\n Graph contained " + count + " nodes!");
|
||||
graph = new AttributedGraph("Aborted", graph.getGraphType(), "Too Many Nodes");
|
||||
graph.addVertex("1", "Graph Aborted");
|
||||
}
|
||||
|
@ -1293,7 +1272,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
|||
graphRenderer.initializeViewer(vv);
|
||||
|
||||
vv.getComponent().requestFocus();
|
||||
vv.setBackground(Color.WHITE);
|
||||
vv.setBackground(BACKGROUND_COLOR);
|
||||
MouseListener[] mouseListeners = vv.getComponent().getMouseListeners();
|
||||
for (MouseListener mouseListener : mouseListeners) {
|
||||
vv.getComponent().removeMouseListener(mouseListener);
|
||||
|
@ -1334,8 +1313,8 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
|||
public void addAction(DockingActionIf action) {
|
||||
|
||||
if (containsAction(action)) {
|
||||
Msg.warn(this, "Action with same name and owner already exixts in graph: " +
|
||||
action.getFullName());
|
||||
Msg.warn(this,
|
||||
"Action with same name and owner already exixts in graph: " + action.getFullName());
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1387,20 +1366,17 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
|||
MutableSelectedState<AttributedVertex> selectedVertexState =
|
||||
viewer.getSelectedVertexState();
|
||||
if (hideSelected && hideUnselected) {
|
||||
viewer.getRenderContext()
|
||||
.setVertexIncludePredicate(v -> false);
|
||||
viewer.getRenderContext().setVertexIncludePredicate(v -> false);
|
||||
}
|
||||
else if (hideSelected) {
|
||||
viewer.getRenderContext()
|
||||
.setVertexIncludePredicate(Predicate.not(selectedVertexState::isSelected));
|
||||
}
|
||||
else if (hideUnselected) {
|
||||
viewer.getRenderContext()
|
||||
.setVertexIncludePredicate(selectedVertexState::isSelected);
|
||||
viewer.getRenderContext().setVertexIncludePredicate(selectedVertexState::isSelected);
|
||||
}
|
||||
else {
|
||||
viewer.getRenderContext()
|
||||
.setVertexIncludePredicate(v -> true);
|
||||
viewer.getRenderContext().setVertexIncludePredicate(v -> true);
|
||||
}
|
||||
viewer.repaint();
|
||||
}
|
||||
|
@ -1467,8 +1443,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
|||
@Override
|
||||
public AttributedVertex getVertex(MouseEvent event) {
|
||||
|
||||
LayoutModel<AttributedVertex> layoutModel =
|
||||
vv.getVisualizationModel().getLayoutModel();
|
||||
LayoutModel<AttributedVertex> layoutModel = vv.getVisualizationModel().getLayoutModel();
|
||||
Point2D p = vv.getTransformSupport().inverseTransform(vv, event.getPoint());
|
||||
AttributedVertex vertex =
|
||||
vv.getPickSupport().getVertex(layoutModel, p.getX(), p.getY());
|
||||
|
@ -1477,8 +1452,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
|||
|
||||
@Override
|
||||
public AttributedEdge getEdge(MouseEvent event) {
|
||||
LayoutModel<AttributedVertex> layoutModel =
|
||||
vv.getVisualizationModel().getLayoutModel();
|
||||
LayoutModel<AttributedVertex> layoutModel = vv.getVisualizationModel().getLayoutModel();
|
||||
Point2D p = vv.getTransformSupport().inverseTransform(vv, event.getPoint());
|
||||
AttributedEdge edge = vv.getPickSupport().getEdge(layoutModel, p.getX(), p.getY());
|
||||
return edge;
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
##MODULE IP: Oxygen Icons - LGPL 3.0
|
||||
##MODULE IP: Tango Icons - Public Domain
|
||||
Module.manifest||GHIDRA||||END|
|
||||
data/programdiff.theme.properties||GHIDRA||||END|
|
||||
src/main/help/help/TOC_Source.xml||GHIDRA||||END|
|
||||
src/main/help/help/shared/arrow.gif||GHIDRA||reviewed||END|
|
||||
src/main/help/help/shared/close16.gif||GHIDRA||reviewed||END|
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
[Defaults]
|
||||
|
||||
color.bg.programdiff.highlight = moccasin
|
||||
|
||||
[Dark Defaults]
|
||||
|
||||
color.bg.programdiff.highlight = darkRed
|
|
@ -26,6 +26,7 @@ import javax.swing.text.*;
|
|||
import javax.swing.tree.TreeSelectionModel;
|
||||
|
||||
import docking.DockingUtils;
|
||||
import docking.theme.GColor;
|
||||
import docking.widgets.EventTrigger;
|
||||
import docking.widgets.OptionDialog;
|
||||
import docking.widgets.fieldpanel.FieldPanel;
|
||||
|
@ -95,7 +96,7 @@ public class ProgramDiffPlugin extends ProgramPlugin
|
|||
private static final String SELECTION_GROUP = "Selection Colors";
|
||||
private static final String DIFF_HIGHLIGHT_COLOR_NAME =
|
||||
SELECTION_GROUP + Options.DELIMITER + "Difference Color";
|
||||
private Color diffHighlightColor = new Color(255, 230, 180); // light orange
|
||||
private Color diffHighlightColor = new GColor("color.bg.programdiff.highlight");
|
||||
private Color cursorHighlightColor;
|
||||
protected static final HelpService help = Help.getHelpService();
|
||||
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
MODULE FILE LICENSE: lib/flatlaf-2.1.jar Apache License 2.0
|
||||
|
|
@ -27,6 +27,7 @@ dependencies {
|
|||
api project(':Generic')
|
||||
api project(':Help')
|
||||
|
||||
api 'com.formdev:flatlaf:2.2'
|
||||
|
||||
// include code from src/test in Generic
|
||||
testImplementation project(path: ':Generic', configuration: 'testArtifacts')
|
||||
|
|
|
@ -21,6 +21,8 @@ src/main/help/help/shared/tip.png||Oxygen Icons - LGPL 3.0|||Oxygen icon theme (
|
|||
src/main/help/help/shared/undo.png||GHIDRA||||END|
|
||||
src/main/help/help/shared/warning.png||Oxygen Icons - LGPL 3.0|||Oxygen icon theme (dual license; LGPL or CC-SA-3.0)|END|
|
||||
src/main/help/help/topics/PlacheholderTopic/Placeholder.htm||GHIDRA||||END|
|
||||
data/docking.palette.theme.properties||GHIDRA||||END|
|
||||
data/docking.theme.properties||GHIDRA||||END|
|
||||
src/main/java/docking/dnd/package.html||GHIDRA||reviewed||END|
|
||||
src/main/java/docking/options/editor/package.html||GHIDRA||reviewed||END|
|
||||
src/main/java/docking/widgets/fieldpanel/package.html||GHIDRA||reviewed||END|
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
ColumnConstraintProvider
|
||||
TypeMapper
|
||||
TableColumn
|
||||
Theme
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
[Defaults]
|
||||
|
||||
color.palette.lightgreen = rgb(127, 255, 127)
|
||||
color.palette.lightred = rgb(255, 127, 127)
|
||||
color.palette.yellow = yellow
|
64
Ghidra/Framework/Docking/data/docking.theme.properties
Normal file
64
Ghidra/Framework/Docking/data/docking.theme.properties
Normal file
|
@ -0,0 +1,64 @@
|
|||
[Defaults]
|
||||
|
||||
color.bg = white
|
||||
color.fg = black
|
||||
color.fg.error = red
|
||||
color.fg.disabled = lightGray
|
||||
|
||||
color.bg.selection = rgb(180, 255, 180) // pale green
|
||||
color.bg.highlight = rgb(255,255,150) // pale yellow
|
||||
|
||||
color.bg.currentline = rgb(232,242,254)
|
||||
color.cursor.focused = red
|
||||
color.cursor.unfocused = pink
|
||||
|
||||
color.bg.table.row = color.bg
|
||||
color.bg.table.row.alt = rgb(237,243,254)
|
||||
|
||||
color.bg.tableheader.gradient.start = color.bg
|
||||
color.bg.tableheader.gradient.end = lightGray
|
||||
color.bg.tableheader.gradient.start.primary = rgb(205, 227, 244)
|
||||
color.bg.tableheader.gradient.end.primary = rgb(126, 186, 233)
|
||||
|
||||
color.bg.textfield.hint.valid = color.bg
|
||||
color.bg.textfield.hint.invalid = rgb(255,225,225)
|
||||
color.fg.textfield.hint = color.fg
|
||||
|
||||
color.bg.selection.help = lightSteelBlue
|
||||
|
||||
// extensions
|
||||
|
||||
color.bg.splash = color.bg
|
||||
color.bg.filechooser = color.bg
|
||||
color.fg.filechooser = color.fg
|
||||
|
||||
color.bg.fieldpanel = color.bg
|
||||
color.fg.fieldpanel = color.fg
|
||||
color.bg.fieldpanel.selection = color.bg.selection
|
||||
color.bg.fieldpanel.highlight = color.bg.highlight
|
||||
color.bg.fieldpanel.selection-highlight = green
|
||||
|
||||
|
||||
|
||||
[Dark Defaults]
|
||||
|
||||
color.bg = rgb(40, 42, 46)
|
||||
color.fg = gray
|
||||
color.bg.currentline = rgb(60,60,70)
|
||||
|
||||
color.cursor.focused = indianRed
|
||||
color.cursor.unfocussed = darkGray
|
||||
|
||||
color.bg.textfield.hint.invalid = maroon
|
||||
|
||||
color.bg.selection = teal
|
||||
color.bg.highlight = rgb(110,110,0)
|
||||
|
||||
color.bg.fieldpanel.select-highlight = darkGreen
|
||||
|
||||
color.bg.tableheader.gradient.start = color.bg
|
||||
color.bg.tableheader.gradient.end = darkGray
|
||||
color.bg.tableheader.gradient.start.primary = color.bg
|
||||
color.bg.tableheader.gradient.end.primary = darkBlue
|
||||
color.bg.table.row.alt = rgb(45,47,65) //TODO
|
||||
|
|
@ -37,7 +37,7 @@ import docking.widgets.list.GList;
|
|||
import docking.widgets.list.GListCellRenderer;
|
||||
import docking.widgets.table.GTableCellRenderer;
|
||||
import docking.widgets.tree.support.GTreeRenderer;
|
||||
import ghidra.docking.util.DockingWindowsLookAndFeelUtils;
|
||||
import ghidra.docking.util.LookAndFeelUtils;
|
||||
import ghidra.util.HTMLUtilities;
|
||||
import resources.ResourceManager;
|
||||
|
||||
|
@ -125,7 +125,7 @@ public class DockingUtils {
|
|||
public static JSeparator createToolbarSeparator() {
|
||||
Dimension sepDim = new Dimension(2, ICON_SIZE + 2);
|
||||
JSeparator separator = new JSeparator(SwingConstants.VERTICAL);
|
||||
if (DockingWindowsLookAndFeelUtils.isUsingAquaUI(separator.getUI())) {
|
||||
if (LookAndFeelUtils.isUsingAquaUI(separator.getUI())) {
|
||||
separator.setUI(new BasicSeparatorUI());
|
||||
}
|
||||
separator.setPreferredSize(sepDim); // ugly work around to force height of separator
|
||||
|
|
|
@ -16,8 +16,8 @@
|
|||
package docking.framework;
|
||||
|
||||
import docking.DockingErrorDisplay;
|
||||
import docking.theme.Gui;
|
||||
import docking.widgets.PopupKeyStorePasswordProvider;
|
||||
import ghidra.docking.util.DockingWindowsLookAndFeelUtils;
|
||||
import ghidra.framework.ApplicationConfiguration;
|
||||
import ghidra.net.ApplicationKeyManagerFactory;
|
||||
import ghidra.util.ErrorDisplay;
|
||||
|
@ -48,7 +48,7 @@ public class DockingApplicationConfiguration extends ApplicationConfiguration {
|
|||
protected void initializeApplication() {
|
||||
super.initializeApplication();
|
||||
|
||||
DockingWindowsLookAndFeelUtils.loadFromPreferences();
|
||||
Gui.initialize();
|
||||
|
||||
if (showSplashScreen) {
|
||||
SplashScreen.showSplashScreen();
|
||||
|
|
|
@ -23,6 +23,7 @@ import javax.swing.*;
|
|||
import javax.swing.border.BevelBorder;
|
||||
|
||||
import docking.*;
|
||||
import docking.theme.GColor;
|
||||
import docking.widgets.label.GDLabel;
|
||||
import docking.widgets.label.GLabel;
|
||||
import generic.util.WindowUtilities;
|
||||
|
@ -37,8 +38,6 @@ import utility.application.ApplicationLayout;
|
|||
*/
|
||||
public class SplashScreen extends JWindow {
|
||||
|
||||
private static final Color DEFAULT_BACKGROUND_COLOR = new Color(243, 250, 255);
|
||||
|
||||
private static SplashScreen splashWindow; // splash window displayed while ghidra is coming up
|
||||
private static DockingFrame hiddenFrame;
|
||||
private static JLabel statusLabel;
|
||||
|
@ -250,7 +249,7 @@ public class SplashScreen extends JWindow {
|
|||
List<Image> list = ApplicationInformationDisplayFactory.getWindowIcons();
|
||||
hiddenFrame.setIconImages(list);
|
||||
hiddenFrame.setUndecorated(true);
|
||||
hiddenFrame.setTransient();
|
||||
hiddenFrame.setTransient();
|
||||
}
|
||||
return hiddenFrame;
|
||||
}
|
||||
|
@ -286,7 +285,7 @@ public class SplashScreen extends JWindow {
|
|||
|
||||
private JPanel createMainPanel() {
|
||||
JPanel mainPanel = new JPanel(new BorderLayout());
|
||||
mainPanel.setBackground(DEFAULT_BACKGROUND_COLOR);
|
||||
mainPanel.setBackground(new GColor("color.bg.splash"));
|
||||
mainPanel.add(createTitlePanel(), BorderLayout.NORTH);
|
||||
mainPanel.add(createContentPanel(), BorderLayout.CENTER);
|
||||
return mainPanel;
|
||||
|
@ -331,7 +330,7 @@ public class SplashScreen extends JWindow {
|
|||
statusLabel.setFont(f);
|
||||
|
||||
statusLabel.setBorder(BorderFactory.createEmptyBorder(0, 10, 2, 10));
|
||||
statusLabel.setBackground(DEFAULT_BACKGROUND_COLOR);
|
||||
statusLabel.setBackground(new GColor("color.bg.splash"));
|
||||
statusLabel.setOpaque(true);
|
||||
return statusLabel;
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ import javax.swing.UIManager;
|
|||
|
||||
import docking.ComponentProvider;
|
||||
import docking.action.DockingActionIf;
|
||||
import docking.theme.GColor;
|
||||
import generic.concurrent.GThreadPool;
|
||||
import generic.util.WindowUtilities;
|
||||
import ghidra.util.*;
|
||||
|
@ -697,7 +698,7 @@ public class HelpManager implements HelpService {
|
|||
* you can see the highlights when you do a search in the JavaHelp.
|
||||
*/
|
||||
private void setColorResources() {
|
||||
UIManager.put("EditorPane.selectionBackground", new Color(204, 204, 255));
|
||||
UIManager.put("EditorPane.selectionBackground", new GColor("color.bg.selection.help"));
|
||||
UIManager.put("EditorPane.selectionForeground", UIManager.get("EditorPane.foreground"));
|
||||
}
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ import javax.swing.KeyStroke;
|
|||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import docking.action.DockingActionIf;
|
||||
import ghidra.docking.util.DockingWindowsLookAndFeelUtils;
|
||||
import ghidra.docking.util.LookAndFeelUtils;
|
||||
import ghidra.util.StringUtilities;
|
||||
|
||||
class DockingToolBarUtils {
|
||||
|
@ -96,7 +96,7 @@ class DockingToolBarUtils {
|
|||
builder.append(InputEvent.getModifiersExText(modifiers));
|
||||
|
||||
// The Aqua LaF does not use the '+' symbol between modifiers
|
||||
if (!DockingWindowsLookAndFeelUtils.isUsingAquaUI(button.getUI())) {
|
||||
if (!LookAndFeelUtils.isUsingAquaUI(button.getUI())) {
|
||||
builder.append('+');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package docking.theme;
|
||||
|
||||
import java.awt.Color;
|
||||
|
||||
import ghidra.util.Msg;
|
||||
import utilities.util.reflection.ReflectionUtilities;
|
||||
|
||||
public class ColorValue extends ThemeValue<Color> {
|
||||
static final String COLOR_ID_PREFIX = "color.";
|
||||
static final String EXTERNAL_PREFIX = "[color]";
|
||||
|
||||
public static final Color LAST_RESORT_DEFAULT = Color.GRAY;
|
||||
|
||||
public ColorValue(String id, Color value) {
|
||||
super(id, null, value);
|
||||
}
|
||||
|
||||
public ColorValue(String id, String refId) {
|
||||
super(id, refId, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ColorValue getReferredValue(GThemeValueMap values, String refId) {
|
||||
return values.getColor(refId);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Color getUnresolvedReferenceValue(String id) {
|
||||
|
||||
Throwable t = ReflectionUtilities.createThrowableWithStackOlderThan();
|
||||
StackTraceElement[] trace = t.getStackTrace();
|
||||
StackTraceElement[] filtered =
|
||||
ReflectionUtilities.filterStackTrace(trace, "docking.theme", "classfinder",
|
||||
"Application", "ghidra.GhidraRun", "java.lang.Class", "java.lang.Thread");
|
||||
t.setStackTrace(filtered);
|
||||
|
||||
Msg.error(this,
|
||||
"Could not resolve indirect color for \"" + id + "\", using last resort default!", t);
|
||||
return LAST_RESORT_DEFAULT;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getIdPrefix() {
|
||||
return COLOR_ID_PREFIX;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toExternalId(String internalId) {
|
||||
if (internalId.startsWith(COLOR_ID_PREFIX)) {
|
||||
return internalId;
|
||||
}
|
||||
return EXTERNAL_PREFIX + internalId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String fromExternalId(String externalId) {
|
||||
if (externalId.startsWith(EXTERNAL_PREFIX)) {
|
||||
return externalId.substring(EXTERNAL_PREFIX.length());
|
||||
}
|
||||
return externalId;
|
||||
}
|
||||
|
||||
public static boolean isColorKey(String key) {
|
||||
return key.startsWith(COLOR_ID_PREFIX) || key.startsWith(EXTERNAL_PREFIX);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int compareValues(Color v1, Color v2) {
|
||||
int alpha1 = v1.getAlpha();
|
||||
int alpha2 = v2.getAlpha();
|
||||
|
||||
if (alpha1 == alpha2) {
|
||||
return getHsbCompareValue(v1) - getHsbCompareValue(v2);
|
||||
}
|
||||
return alpha1 - alpha2;
|
||||
}
|
||||
|
||||
private int getHsbCompareValue(Color v) {
|
||||
// compute a value the compares colors first by hue, then saturation, then brightness
|
||||
// reduce noise by converting float values from 0-1 to integers 0 - 7
|
||||
float[] hsb = Color.RGBtoHSB(v.getRed(), v.getGreen(), v.getBlue(), null);
|
||||
return 100 * (int) (10 * hsb[0]) + 10 * (int) (10 * hsb[1]) + (int) (10 * hsb[2]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package docking.theme;
|
||||
|
||||
import ghidra.docking.util.LookAndFeelUtils;
|
||||
|
||||
public class DefaultTheme extends DiscoverableGTheme {
|
||||
|
||||
public DefaultTheme() {
|
||||
super("Default", LookAndFeelUtils.getDefaultLookAndFeelName());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package docking.theme;
|
||||
|
||||
import ghidra.util.classfinder.ExtensionPoint;
|
||||
|
||||
public abstract class DiscoverableGTheme extends GTheme implements ExtensionPoint {
|
||||
static final String CLASS_PREFIX = "Class:";
|
||||
|
||||
protected DiscoverableGTheme(String name, String lookAndFeelName) {
|
||||
super(name, lookAndFeelName, false);
|
||||
}
|
||||
|
||||
protected DiscoverableGTheme(String name, String lookAndFeelName, boolean isDark) {
|
||||
super(name, lookAndFeelName, isDark);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getThemeLocater() {
|
||||
return CLASS_PREFIX + getClass().getName();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package docking.theme;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
public class FileGTheme extends GTheme {
|
||||
public static final String FILE_PREFIX = "File:";
|
||||
private final File file;
|
||||
|
||||
public FileGTheme(File file) throws IOException {
|
||||
this(file, new ThemeReader(file));
|
||||
}
|
||||
|
||||
FileGTheme(File file, ThemeReader reader) {
|
||||
super(reader.getThemeName(), reader.getLookAndFeelName(), reader.isDark());
|
||||
this.file = file;
|
||||
reader.loadValues(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getThemeLocater() {
|
||||
return FILE_PREFIX + file.getAbsolutePath();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package docking.theme;
|
||||
|
||||
import java.awt.Font;
|
||||
|
||||
import ghidra.util.Msg;
|
||||
|
||||
public class FontValue extends ThemeValue<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 FontValue(String id, Font value) {
|
||||
super(id, null, value);
|
||||
}
|
||||
|
||||
public FontValue(String id, String refId) {
|
||||
super(id, refId, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected FontValue getReferredValue(GThemeValueMap values, String refId) {
|
||||
return values.getFont(refId);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Font getUnresolvedReferenceValue(String id) {
|
||||
Msg.warn(this, "Could not resolve indirect font for" + id + ", using last resort default");
|
||||
return LAST_RESORT_DEFAULT;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getIdPrefix() {
|
||||
return FONT_ID_PREFIX;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toExternalId(String internalId) {
|
||||
if (internalId.startsWith(FONT_ID_PREFIX)) {
|
||||
return internalId;
|
||||
}
|
||||
return EXTERNAL_PREFIX + internalId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String fromExternalId(String externalId) {
|
||||
if (externalId.startsWith(EXTERNAL_PREFIX)) {
|
||||
return externalId.substring(EXTERNAL_PREFIX.length());
|
||||
}
|
||||
return externalId;
|
||||
}
|
||||
|
||||
public static boolean isFontKey(String key) {
|
||||
return key.startsWith(FONT_ID_PREFIX) || key.startsWith(EXTERNAL_PREFIX);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int compareValues(Font v1, Font v2) {
|
||||
return v1.toString().compareTo(v2.toString());
|
||||
}
|
||||
}
|
145
Ghidra/Framework/Docking/src/main/java/docking/theme/GColor.java
Normal file
145
Ghidra/Framework/Docking/src/main/java/docking/theme/GColor.java
Normal file
|
@ -0,0 +1,145 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package docking.theme;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.awt.image.ColorModel;
|
||||
|
||||
public class GColor extends Color implements Refreshable {
|
||||
|
||||
private String id;
|
||||
private Color delegate;
|
||||
|
||||
public GColor(String id) {
|
||||
super(0x808080);
|
||||
this.id = id;
|
||||
delegate = Gui.getRawColor(id);
|
||||
if (delegate == null) {
|
||||
delegate = Color.gray;
|
||||
}
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRed() {
|
||||
return delegate.getRed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getGreen() {
|
||||
return delegate.getGreen();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBlue() {
|
||||
return delegate.getBlue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAlpha() {
|
||||
return delegate.getAlpha();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRGB() {
|
||||
return delegate.getRGB();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Color brighter() {
|
||||
return delegate.brighter();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Color darker() {
|
||||
return delegate.darker();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return delegate.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return delegate.equals(obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getName() + " [id = " + id + ", " + delegate.toString() + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public float[] getRGBComponents(float[] compArray) {
|
||||
return delegate.getRGBComponents(compArray);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float[] getRGBColorComponents(float[] compArray) {
|
||||
return delegate.getRGBColorComponents(compArray);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float[] getComponents(float[] compArray) {
|
||||
return delegate.getColorComponents(compArray);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float[] getColorComponents(float[] compArray) {
|
||||
return delegate.getColorComponents(compArray);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float[] getComponents(ColorSpace cspace, float[] compArray) {
|
||||
return delegate.getComponents(cspace, compArray);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float[] getColorComponents(ColorSpace cspace, float[] compArray) {
|
||||
return delegate.getColorComponents(cspace, compArray);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ColorSpace getColorSpace() {
|
||||
return delegate.getColorSpace();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized PaintContext createContext(ColorModel cm, Rectangle r, Rectangle2D r2d,
|
||||
AffineTransform xform, RenderingHints hints) {
|
||||
return delegate.createContext(cm, r, r2d, xform, hints);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTransparency() {
|
||||
return delegate.getTransparency();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refresh() {
|
||||
Color color = Gui.getRawColor(id);
|
||||
if (color != null) {
|
||||
delegate = color;
|
||||
}
|
||||
}
|
||||
}
|
310
Ghidra/Framework/Docking/src/main/java/docking/theme/GFont.java
Normal file
310
Ghidra/Framework/Docking/src/main/java/docking/theme/GFont.java
Normal file
|
@ -0,0 +1,310 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package docking.theme;
|
||||
|
||||
import java.awt.Font;
|
||||
import java.awt.font.*;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.text.AttributedCharacterIterator.Attribute;
|
||||
import java.text.CharacterIterator;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
public class GFont extends Font implements Refreshable {
|
||||
|
||||
private String id;
|
||||
private Font delegate;
|
||||
|
||||
public GFont(String id) {
|
||||
super("Courier", Font.PLAIN, 12);
|
||||
this.id = id;
|
||||
delegate = Gui.getRawFont(id);
|
||||
if (delegate == null) {
|
||||
delegate = new Font("Courier", Font.PLAIN, 12);
|
||||
}
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AffineTransform getTransform() {
|
||||
return delegate.getTransform();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refresh() {
|
||||
Font font = Gui.getRawFont(id);
|
||||
if (font != null) {
|
||||
delegate = font;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFamily() {
|
||||
return delegate.getFamily();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFamily(Locale l) {
|
||||
return delegate.getFamily(l);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPSName() {
|
||||
return delegate.getPSName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return delegate.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFontName() {
|
||||
return delegate.getFontName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFontName(Locale l) {
|
||||
return delegate.getFontName(l);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getStyle() {
|
||||
|
||||
return delegate.getStyle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSize() {
|
||||
return delegate.getSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getSize2D() {
|
||||
return delegate.getSize2D();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPlain() {
|
||||
return delegate.isPlain();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBold() {
|
||||
return delegate.isBold();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isItalic() {
|
||||
return delegate.isItalic();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTransformed() {
|
||||
return delegate.isTransformed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasLayoutAttributes() {
|
||||
return delegate.hasLayoutAttributes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return delegate.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return delegate.equals(obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return delegate.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumGlyphs() {
|
||||
return delegate.getNumGlyphs();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMissingGlyphCode() {
|
||||
return delegate.getMissingGlyphCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getBaselineFor(char c) {
|
||||
return delegate.getBaselineFor(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<TextAttribute, ?> getAttributes() {
|
||||
return delegate.getAttributes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Attribute[] getAvailableAttributes() {
|
||||
return delegate.getAvailableAttributes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Font deriveFont(int newStyle, float newSize) {
|
||||
return delegate.deriveFont(newStyle, newSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Font deriveFont(int newStyle, AffineTransform trans) {
|
||||
return delegate.deriveFont(newStyle, trans);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Font deriveFont(float newSize) {
|
||||
return delegate.deriveFont(newSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Font deriveFont(AffineTransform trans) {
|
||||
return delegate.deriveFont(trans);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Font deriveFont(int newStyle) {
|
||||
return delegate.deriveFont(newStyle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Font deriveFont(Map<? extends Attribute, ?> attributes) {
|
||||
return delegate.deriveFont(attributes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canDisplay(char c) {
|
||||
return delegate.canDisplay(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canDisplay(int codePoint) {
|
||||
return delegate.canDisplay(codePoint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int canDisplayUpTo(String str) {
|
||||
return delegate.canDisplayUpTo(str);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int canDisplayUpTo(char[] text, int start, int limit) {
|
||||
return delegate.canDisplayUpTo(text, start, limit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int canDisplayUpTo(CharacterIterator iter, int start, int limit) {
|
||||
return delegate.canDisplayUpTo(iter, start, limit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getItalicAngle() {
|
||||
return delegate.getItalicAngle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasUniformLineMetrics() {
|
||||
return delegate.hasUniformLineMetrics();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LineMetrics getLineMetrics(String str, FontRenderContext frc) {
|
||||
return delegate.getLineMetrics(str, frc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LineMetrics getLineMetrics(String str, int beginIndex, int limit,
|
||||
FontRenderContext frc) {
|
||||
return delegate.getLineMetrics(str, beginIndex, limit, frc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LineMetrics getLineMetrics(char[] chars, int beginIndex, int limit,
|
||||
FontRenderContext frc) {
|
||||
return delegate.getLineMetrics(chars, beginIndex, limit, frc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LineMetrics getLineMetrics(CharacterIterator ci, int beginIndex, int limit,
|
||||
FontRenderContext frc) {
|
||||
return delegate.getLineMetrics(ci, beginIndex, limit, frc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rectangle2D getStringBounds(String str, FontRenderContext frc) {
|
||||
return delegate.getStringBounds(str, frc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rectangle2D getStringBounds(String str, int beginIndex, int limit,
|
||||
FontRenderContext frc) {
|
||||
return delegate.getStringBounds(str, beginIndex, limit, frc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rectangle2D getStringBounds(char[] chars, int beginIndex, int limit,
|
||||
FontRenderContext frc) {
|
||||
return delegate.getStringBounds(chars, beginIndex, limit, frc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rectangle2D getStringBounds(CharacterIterator ci, int beginIndex, int limit,
|
||||
FontRenderContext frc) {
|
||||
return delegate.getStringBounds(ci, beginIndex, limit, frc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rectangle2D getMaxCharBounds(FontRenderContext frc) {
|
||||
return delegate.getMaxCharBounds(frc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GlyphVector createGlyphVector(FontRenderContext frc, String str) {
|
||||
return delegate.createGlyphVector(frc, str);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GlyphVector createGlyphVector(FontRenderContext frc, char[] chars) {
|
||||
return delegate.createGlyphVector(frc, chars);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GlyphVector createGlyphVector(FontRenderContext frc, CharacterIterator ci) {
|
||||
return delegate.createGlyphVector(frc, ci);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GlyphVector createGlyphVector(FontRenderContext frc, int[] glyphCodes) {
|
||||
return delegate.createGlyphVector(frc, glyphCodes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GlyphVector layoutGlyphVector(FontRenderContext frc, char[] text, int start, int limit,
|
||||
int flags) {
|
||||
return delegate.layoutGlyphVector(frc, text, start, limit, flags);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package docking.theme;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Graphics;
|
||||
|
||||
import javax.swing.Icon;
|
||||
|
||||
import resources.ResourceManager;
|
||||
|
||||
public class GIcon implements Icon, Refreshable {
|
||||
|
||||
private String id;
|
||||
private Icon delegate;
|
||||
|
||||
public GIcon(String id) {
|
||||
this.id = id;
|
||||
delegate = Gui.getRawIcon(id);
|
||||
if (delegate == null) {
|
||||
delegate = ResourceManager.getDefaultIcon();
|
||||
}
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paintIcon(Component c, Graphics g, int x, int y) {
|
||||
delegate.paintIcon(c, g, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIconWidth() {
|
||||
return delegate.getIconWidth();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIconHeight() {
|
||||
return delegate.getIconHeight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refresh() {
|
||||
Icon icon = Gui.getRawIcon(id);
|
||||
if (icon != null) {
|
||||
delegate = icon;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
281
Ghidra/Framework/Docking/src/main/java/docking/theme/GTheme.java
Normal file
281
Ghidra/Framework/Docking/src/main/java/docking/theme/GTheme.java
Normal file
|
@ -0,0 +1,281 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package docking.theme;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Font;
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.docking.util.LookAndFeelUtils;
|
||||
import ghidra.util.WebColors;
|
||||
|
||||
/**
|
||||
* Class to store all the configurable appearance properties (Colors, Fonts, Icons, Look and Feel)
|
||||
* in an application.
|
||||
*/
|
||||
public class GTheme extends GThemeValueMap {
|
||||
static final String THEME_NAME_KEY = "name";
|
||||
static final String THEME_LOOK_AND_FEEL_KEY = "lookAndFeel";
|
||||
static final String THEME_IS_DARK_KEY = "dark";
|
||||
|
||||
private final String name;
|
||||
private final String lookAndFeelName;
|
||||
private final boolean isDark;
|
||||
|
||||
public GTheme(String name) {
|
||||
this(name, LookAndFeelUtils.SYSTEM, false);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new empty GTheme with the given name
|
||||
* @param name the name for the new GTheme
|
||||
* @param lookAndFeelName the look and feel used by this theme
|
||||
* @param isDark true if this theme uses dark backgrounds instead of the standard
|
||||
* light backgrounds
|
||||
*/
|
||||
protected GTheme(String name, String lookAndFeelName, boolean isDark) {
|
||||
this.name = name;
|
||||
this.lookAndFeelName = lookAndFeelName;
|
||||
this.isDark = isDark;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of this GTheme
|
||||
* @return the name of this GTheme
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the LookAndFeel associated with this GTheme
|
||||
* @return the name of the LookAndFeel associated with this GTheme
|
||||
*/
|
||||
public String getLookAndFeelName() {
|
||||
return lookAndFeelName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this theme should use dark defaults
|
||||
* @return true if this theme should use dark defaults
|
||||
*/
|
||||
public boolean isDark() {
|
||||
return isDark;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a String that can be used to find and restore this theme.
|
||||
* @return a String that can be used to find and restore this theme.
|
||||
*/
|
||||
public String getThemeLocater() {
|
||||
return "Default";
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Color for the given id
|
||||
* @param id the id to associate with the given Color
|
||||
* @param color the Color to associate with the given id
|
||||
*/
|
||||
public void setColor(String id, Color color) {
|
||||
addColor(new ColorValue(id, color));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a referred Color for the given id
|
||||
* @param id the id to associate with the refId
|
||||
* @param refId the id of an indirect Color lookup for the given id.
|
||||
*/
|
||||
public void setColorRef(String id, String refId) {
|
||||
addColor(new ColorValue(id, refId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Font for the given id
|
||||
* @param id the id to associate with the given Font
|
||||
* @param font the Font to associate with the given id
|
||||
*/
|
||||
public void setFont(String id, Font font) {
|
||||
addFont(new FontValue(id, font));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a referred font for the given id
|
||||
* @param id the id to associate with the given Font reference id
|
||||
* @param refId the id of an indirect Font lookup for the given id.
|
||||
*/
|
||||
public void setFontRef(String id, String refId) {
|
||||
addFont(new FontValue(id, refId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the icon for the given id
|
||||
* @param id the id to associate with the given IconPath
|
||||
* @param iconPath the path of the icon to assign to the given id
|
||||
*/
|
||||
public void setIcon(String id, String iconPath) {
|
||||
addIconPath(new IconValue(id, null, iconPath));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a referred icon id for the given id
|
||||
* @param id the id to associate with the given Font
|
||||
* @param refId the id of an indirect Icon lookup for the given id.
|
||||
*/
|
||||
public void setIconRef(String id, String refId) {
|
||||
addIconPath(new IconValue(id, refId, null));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return name.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
GTheme other = (GTheme) obj;
|
||||
return Objects.equals(name, other.name) &&
|
||||
Objects.equals(lookAndFeelName, other.lookAndFeelName) &&
|
||||
Objects.equals(isDark, other.isDark);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new file based GTheme with the same values as this GTheme
|
||||
* @param saveToFile file to associate and save this GTheme to
|
||||
* @return the new theme
|
||||
* @throws IOException if a general I/O exception occurs
|
||||
*/
|
||||
public GTheme saveToFile(File saveToFile) throws IOException {
|
||||
return doSaveToFile(saveToFile, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new file based GTheme with the same values as this GTheme and includes default
|
||||
* values not modified by this theme.
|
||||
* @param saveToFile file to associate and save this GTheme to
|
||||
* @param defaults the collection of default values to include in the output file
|
||||
* @return the new theme
|
||||
* @throws IOException if a general I/O exception occurs
|
||||
*/
|
||||
public GTheme saveToFile(File saveToFile, GThemeValueMap defaults) throws IOException {
|
||||
GThemeValueMap combined = new GThemeValueMap();
|
||||
combined.load(defaults);
|
||||
combined.load(this);
|
||||
return doSaveToFile(saveToFile, combined);
|
||||
}
|
||||
|
||||
private GTheme doSaveToFile(File saveToFile, GThemeValueMap values) throws IOException {
|
||||
try (BufferedWriter writer = new BufferedWriter(new FileWriter(saveToFile))) {
|
||||
List<ColorValue> colors = values.getColors();
|
||||
Collections.sort(colors);
|
||||
|
||||
List<FontValue> fonts = values.getFonts();
|
||||
Collections.sort(fonts);
|
||||
|
||||
List<IconValue> icons = values.getIconPaths();
|
||||
Collections.sort(icons);
|
||||
|
||||
writer.write(THEME_NAME_KEY + " = " + name);
|
||||
writer.newLine();
|
||||
|
||||
writer.write(THEME_LOOK_AND_FEEL_KEY + " = " + lookAndFeelName);
|
||||
writer.newLine();
|
||||
|
||||
if (isDark()) {
|
||||
writer.write(THEME_IS_DARK_KEY + " = true");
|
||||
writer.newLine();
|
||||
}
|
||||
|
||||
for (ColorValue colorValue : colors) {
|
||||
String outputId = colorValue.toExternalId(colorValue.getId());
|
||||
writer.write(outputId + " = " + getValueOutput(colorValue));
|
||||
writer.newLine();
|
||||
}
|
||||
|
||||
for (FontValue fontValue : fonts) {
|
||||
String outputId = fontValue.toExternalId(fontValue.getId());
|
||||
writer.write(outputId + " = " + getValueOutput(fontValue));
|
||||
writer.newLine();
|
||||
}
|
||||
|
||||
for (IconValue iconValue : icons) {
|
||||
String outputId = iconValue.toExternalId(iconValue.getId());
|
||||
writer.write(outputId + " = " + getValueOutput(iconValue));
|
||||
writer.newLine();
|
||||
}
|
||||
|
||||
}
|
||||
return new FileGTheme(saveToFile);
|
||||
}
|
||||
|
||||
private String getValueOutput(IconValue iconValue) {
|
||||
if (iconValue.getReferenceId() != null) {
|
||||
return iconValue.toExternalId(iconValue.getReferenceId());
|
||||
}
|
||||
return iconValue.getRawValue();
|
||||
}
|
||||
|
||||
private String getValueOutput(FontValue fontValue) {
|
||||
if (fontValue.getReferenceId() != null) {
|
||||
return fontValue.toExternalId(fontValue.getReferenceId());
|
||||
}
|
||||
Font font = fontValue.getRawValue();
|
||||
return String.format("%s-%s-%s", font.getName(), getStyleString(font), font.getSize());
|
||||
}
|
||||
|
||||
private String getStyleString(Font font) {
|
||||
boolean bold = font.isBold();
|
||||
boolean italic = font.isItalic();
|
||||
if (bold && italic) {
|
||||
return "BOLDITALIC";
|
||||
}
|
||||
if (bold) {
|
||||
return "BOLD";
|
||||
}
|
||||
if (italic) {
|
||||
return "ITALIC";
|
||||
}
|
||||
return "PLAIN";
|
||||
}
|
||||
|
||||
private String getValueOutput(ColorValue colorValue) {
|
||||
if (colorValue.getReferenceId() != null) {
|
||||
return colorValue.toExternalId(colorValue.getReferenceId());
|
||||
}
|
||||
Color color = colorValue.getRawValue();
|
||||
String outputString = WebColors.toString(color, false);
|
||||
String colorName = WebColors.toWebColorName(color);
|
||||
if (colorName != null) {
|
||||
outputString += " // " + colorName;
|
||||
}
|
||||
return outputString;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package docking.theme;
|
||||
|
||||
import java.awt.Color;
|
||||
|
||||
/** TODO doc how clients should use this in their code, with
|
||||
*
|
||||
*
|
||||
* Colors.BACKGROUND
|
||||
* Colors.Java.BORDER
|
||||
*/
|
||||
public class GThemeDefaults {
|
||||
public static final String STANDARD_DEFAULTS = "Standard Defaults"; // core defaults map name
|
||||
public static final String DARK = "Dark"; // defaults map name for dark based themes
|
||||
|
||||
public static class Ids {
|
||||
|
||||
public static final String COLOR_BG = "color.bg"; // TODO
|
||||
|
||||
public static class Java {
|
||||
public static final String BORDER = "Component.borderColor"; // TODO
|
||||
}
|
||||
}
|
||||
|
||||
public static class Colors {
|
||||
//@formatter:off
|
||||
public static final GColor BACKGROUND = new GColor("color.bg");
|
||||
//@formatter:on
|
||||
|
||||
public static class Java {
|
||||
public static final Color BORDER = new GColor(Ids.Java.BORDER);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package docking.theme;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Component;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import docking.DialogComponentProvider;
|
||||
import docking.widgets.filechooser.GhidraFileChooser;
|
||||
import docking.widgets.filechooser.GhidraFileChooserMode;
|
||||
import docking.widgets.table.GFilterTable;
|
||||
import docking.widgets.table.GTable;
|
||||
import ghidra.util.filechooser.GhidraFileFilter;
|
||||
|
||||
public class GThemeDialog extends DialogComponentProvider {
|
||||
|
||||
public GThemeDialog() {
|
||||
super("Theme Dialog");
|
||||
addWorkPanel(createMainPanel());
|
||||
addOKButton();
|
||||
addCancelButton();
|
||||
setOkButtonText("Save");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void okCallback() {
|
||||
GhidraFileChooser chooser = new GhidraFileChooser(getComponent());
|
||||
chooser.setTitle("Choose Theme File");
|
||||
chooser.setApproveButtonText("Select Output File");
|
||||
chooser.setApproveButtonToolTipText("Select File");
|
||||
chooser.setFileSelectionMode(GhidraFileChooserMode.FILES_ONLY);
|
||||
chooser.setSelectedFileFilter(GhidraFileFilter.ALL);
|
||||
File file = chooser.getSelectedFile();
|
||||
try {
|
||||
Gui.getActiveTheme().saveToFile(file, Gui.getAllDefaultValues());
|
||||
}
|
||||
catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private JComponent createMainPanel() {
|
||||
JPanel panel = new JPanel();
|
||||
|
||||
panel.setLayout(new BorderLayout());
|
||||
|
||||
panel.add(buildTabedTables());
|
||||
return panel;
|
||||
}
|
||||
|
||||
private Component buildTabedTables() {
|
||||
JTabbedPane tabbedPane = new JTabbedPane();
|
||||
tabbedPane.add("Colors", buildColorTable());
|
||||
return tabbedPane;
|
||||
}
|
||||
|
||||
private JComponent buildColorTable() {
|
||||
ThemeColorTableModel colorTableModel = new ThemeColorTableModel(Gui.getActiveTheme());
|
||||
|
||||
GTable colorTable = new GTable(colorTableModel);
|
||||
colorTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
GFilterTable<ColorValue> filterTable = new GFilterTable<>(colorTableModel);
|
||||
filterTable.getTable().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
return filterTable;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package docking.theme;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class GThemeValueMap {
|
||||
Map<String, ColorValue> colorMap = new HashMap<>();
|
||||
Map<String, FontValue> fontMap = new HashMap<>();
|
||||
Map<String, IconValue> iconMap = new HashMap<>();
|
||||
|
||||
public GThemeValueMap() {
|
||||
}
|
||||
|
||||
public GThemeValueMap(GThemeValueMap initial) {
|
||||
load(initial);
|
||||
}
|
||||
|
||||
public void addColor(ColorValue value) {
|
||||
if (value != null) {
|
||||
colorMap.put(value.getId(), value);
|
||||
}
|
||||
}
|
||||
|
||||
public void addFont(FontValue value) {
|
||||
if (value != null) {
|
||||
fontMap.put(value.getId(), value);
|
||||
}
|
||||
}
|
||||
|
||||
public void addIconPath(IconValue value) {
|
||||
if (value != null) {
|
||||
iconMap.put(value.getId(), value);
|
||||
}
|
||||
}
|
||||
|
||||
public ColorValue getColor(String id) {
|
||||
return colorMap.get(id);
|
||||
}
|
||||
|
||||
public FontValue getFont(String id) {
|
||||
return fontMap.get(id);
|
||||
}
|
||||
|
||||
public IconValue getIcon(String id) {
|
||||
return iconMap.get(id);
|
||||
}
|
||||
|
||||
public void load(GThemeValueMap valueMap) {
|
||||
valueMap.colorMap.values().forEach(v -> addColor(v));
|
||||
valueMap.fontMap.values().forEach(v -> addFont(v));
|
||||
valueMap.iconMap.values().forEach(v -> addIconPath(v));
|
||||
|
||||
}
|
||||
|
||||
public List<ColorValue> getColors() {
|
||||
return new ArrayList<>(colorMap.values());
|
||||
}
|
||||
|
||||
public List<FontValue> getFonts() {
|
||||
return new ArrayList<>(fontMap.values());
|
||||
}
|
||||
|
||||
public List<IconValue> getIconPaths() {
|
||||
return new ArrayList<>(iconMap.values());
|
||||
}
|
||||
|
||||
public boolean containsColor(String id) {
|
||||
return colorMap.containsKey(id);
|
||||
}
|
||||
|
||||
public boolean containsFont(String id) {
|
||||
return colorMap.containsKey(id);
|
||||
}
|
||||
|
||||
public boolean containsIconPath(String id) {
|
||||
return colorMap.containsKey(id);
|
||||
}
|
||||
|
||||
public Object size() {
|
||||
return colorMap.size() + fontMap.size() + iconMap.size();
|
||||
}
|
||||
}
|
310
Ghidra/Framework/Docking/src/main/java/docking/theme/Gui.java
Normal file
310
Ghidra/Framework/Docking/src/main/java/docking/theme/Gui.java
Normal file
|
@ -0,0 +1,310 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package docking.theme;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import docking.framework.ApplicationInformationDisplayFactory;
|
||||
import ghidra.docking.util.LookAndFeelUtils;
|
||||
import ghidra.framework.Application;
|
||||
import ghidra.framework.preferences.Preferences;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.classfinder.ClassSearcher;
|
||||
import resources.ResourceManager;
|
||||
import utilities.util.reflection.ReflectionUtilities;
|
||||
|
||||
// TODO doc what this concept is
|
||||
public class Gui {
|
||||
|
||||
public static final String BACKGROUND_KEY = "color.bg.text";
|
||||
|
||||
private static final String THEME_PREFFERENCE_KEY = "Theme";
|
||||
|
||||
private static GTheme activeTheme = new DefaultTheme();
|
||||
private static Set<GTheme> allThemes;
|
||||
|
||||
private static GThemeValueMap ghidraCoreDefaults = new GThemeValueMap();
|
||||
private static GThemeValueMap javaDefaults;
|
||||
private static GThemeValueMap currentValues = new GThemeValueMap();
|
||||
|
||||
private static GThemeValueMap darkDefaults = new GThemeValueMap();
|
||||
|
||||
private static ThemePropertiesLoader themePropertiesLoader = new ThemePropertiesLoader();
|
||||
|
||||
static void setPropertiesLoader(ThemePropertiesLoader loader) {
|
||||
themePropertiesLoader = loader;
|
||||
}
|
||||
|
||||
private Gui() {
|
||||
// static utils class, can't construct
|
||||
}
|
||||
|
||||
public static void initialize() {
|
||||
themePropertiesLoader.initialize();
|
||||
loadThemeDefaults();
|
||||
setTheme(getThemeFromPreferences());
|
||||
LookAndFeelUtils.installGlobalOverrides();
|
||||
platformSpecificFixups();
|
||||
}
|
||||
|
||||
private static void loadThemeDefaults() {
|
||||
ghidraCoreDefaults = themePropertiesLoader.getDefaults();
|
||||
darkDefaults = themePropertiesLoader.getDarkDefaults();
|
||||
}
|
||||
|
||||
public static void setTheme(GTheme theme) {
|
||||
activeTheme = theme;
|
||||
LookAndFeelUtils.setLookAndFeel(theme.getLookAndFeelName());
|
||||
javaDefaults = mineJavaDefaults();
|
||||
currentValues = buildCurrentValues(theme);
|
||||
installBackIntoJava();
|
||||
}
|
||||
|
||||
private static void installBackIntoJava() {
|
||||
UIDefaults defaults = UIManager.getDefaults();
|
||||
for (ColorValue color : javaDefaults.getColors()) {
|
||||
String id = color.getId();
|
||||
defaults.put(id, new GColor(id));
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isJavaDefinedColor(String id) {
|
||||
return javaDefaults.containsColor(id);
|
||||
}
|
||||
|
||||
public static GThemeValueMap getAllValues() {
|
||||
return new GThemeValueMap(currentValues);
|
||||
}
|
||||
|
||||
public static GThemeValueMap getAllDefaultValues() {
|
||||
GThemeValueMap currentDefaults = new GThemeValueMap();
|
||||
currentDefaults.load(javaDefaults);
|
||||
currentDefaults.load(ghidraCoreDefaults);
|
||||
if (activeTheme.isDark()) {
|
||||
currentDefaults.load(darkDefaults);
|
||||
}
|
||||
return currentDefaults;
|
||||
}
|
||||
|
||||
public static Set<GTheme> getAllThemes() {
|
||||
if (allThemes == null) {
|
||||
allThemes = findThemes();
|
||||
}
|
||||
return Collections.unmodifiableSet(allThemes);
|
||||
}
|
||||
|
||||
public static Color darker(Color color) {
|
||||
if (activeTheme.isDark()) {
|
||||
return color.brighter();
|
||||
}
|
||||
return color.darker();
|
||||
}
|
||||
|
||||
public static Color brighter(Color color) {
|
||||
if (activeTheme.isDark()) {
|
||||
return color.darker();
|
||||
}
|
||||
return color.brighter();
|
||||
}
|
||||
|
||||
public static GFont getFont(String id) {
|
||||
return new GFont(id);
|
||||
}
|
||||
|
||||
public static GIcon getIcon(String id) {
|
||||
return new GIcon(id);
|
||||
}
|
||||
|
||||
public static void saveThemeToPreferneces(GTheme theme) {
|
||||
Preferences.setProperty(THEME_PREFFERENCE_KEY, theme.getThemeLocater());
|
||||
Preferences.store();
|
||||
}
|
||||
|
||||
public static GTheme getActiveTheme() {
|
||||
return activeTheme;
|
||||
}
|
||||
|
||||
public static String getLookAndFeelName() {
|
||||
return activeTheme.getLookAndFeelName();
|
||||
}
|
||||
|
||||
private static void platformSpecificFixups() {
|
||||
|
||||
// Set the dock icon for macOS
|
||||
if (Taskbar.isTaskbarSupported()) {
|
||||
Taskbar taskbar = Taskbar.getTaskbar();
|
||||
if (taskbar.isSupported(Taskbar.Feature.ICON_IMAGE)) {
|
||||
taskbar.setIconImage(ApplicationInformationDisplayFactory.getLargestWindowIcon());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Color getRawColor(String id) {
|
||||
ColorValue color = currentValues.getColor(id);
|
||||
if (color == null) {
|
||||
Throwable t = getFilteredTrace();
|
||||
|
||||
Msg.error(Gui.class, "No color value registered for: " + id, t);
|
||||
return null;
|
||||
}
|
||||
return color.get(currentValues);
|
||||
}
|
||||
|
||||
static Font getRawFont(String id) {
|
||||
FontValue font = currentValues.getFont(id);
|
||||
if (font == null) {
|
||||
Throwable t = getFilteredTrace();
|
||||
|
||||
Msg.error(Gui.class, "No font value registered for: " + id, t);
|
||||
return null;
|
||||
}
|
||||
return font.get(currentValues);
|
||||
}
|
||||
|
||||
public static Icon getRawIcon(String id) {
|
||||
IconValue icon = currentValues.getIcon(id);
|
||||
if (icon == null) {
|
||||
Throwable t = getFilteredTrace();
|
||||
|
||||
Msg.error(Gui.class, "No color value registered for: " + id, t);
|
||||
return null;
|
||||
}
|
||||
String iconPath = icon.get(currentValues);
|
||||
return ResourceManager.loadImage(iconPath);
|
||||
}
|
||||
|
||||
private static Throwable getFilteredTrace() {
|
||||
Throwable t = ReflectionUtilities.createThrowableWithStackOlderThan();
|
||||
StackTraceElement[] trace = t.getStackTrace();
|
||||
StackTraceElement[] filtered =
|
||||
ReflectionUtilities.filterStackTrace(trace, "java.", "theme.Gui", "theme.GColor");
|
||||
t.setStackTrace(filtered);
|
||||
return t;
|
||||
}
|
||||
|
||||
private static GThemeValueMap buildCurrentValues(GTheme theme) {
|
||||
GThemeValueMap map = new GThemeValueMap();
|
||||
|
||||
map.load(javaDefaults);
|
||||
map.load(ghidraCoreDefaults);
|
||||
if (theme.isDark()) {
|
||||
map.load(darkDefaults);
|
||||
}
|
||||
map.load(theme);
|
||||
return map;
|
||||
}
|
||||
|
||||
private static GThemeValueMap mineJavaDefaults() {
|
||||
GThemeValueMap values = new GThemeValueMap();
|
||||
// for now, just doing color properties.
|
||||
List<String> ids = LookAndFeelUtils.getLookAndFeelIdsForType(Color.class);
|
||||
for (String id : ids) {
|
||||
// Create a new color to ensure we are not storing a UIResource; otherwise java
|
||||
// java ignore the color because the UI widgets take liberties when UIResources
|
||||
// are being used.
|
||||
Color lafColor = new Color(UIManager.getColor(id).getRGB(), true);
|
||||
values.addColor(new ColorValue(id, lafColor));
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
private static Set<GTheme> findThemes() {
|
||||
Set<GTheme> set = new HashSet<>();
|
||||
set.addAll(findDiscoverableThemes());
|
||||
set.addAll(loadThemesFromFiles());
|
||||
|
||||
// The set should contains a duplicate of the active theme. Make sure the active theme
|
||||
// instance is the one in the set
|
||||
set.remove(activeTheme);
|
||||
set.add(activeTheme);
|
||||
return set;
|
||||
}
|
||||
|
||||
private static Collection<GTheme> loadThemesFromFiles() {
|
||||
List<File> fileList = new ArrayList<>();
|
||||
|
||||
File dir = Application.getUserSettingsDirectory();
|
||||
FileFilter themeFileFilter = file -> file.getName().endsWith(".theme");
|
||||
fileList.addAll(Arrays.asList(dir.listFiles(themeFileFilter)));
|
||||
|
||||
List<GTheme> list = new ArrayList<>();
|
||||
for (File file : fileList) {
|
||||
GTheme theme = loadTheme(file);
|
||||
if (theme != null) {
|
||||
list.add(theme);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private static GTheme loadTheme(File file) {
|
||||
try {
|
||||
return new FileGTheme(file);
|
||||
}
|
||||
catch (IOException e) {
|
||||
Msg.error(Gui.class, "Could not load theme from file: " + file.getAbsolutePath());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Collection<DiscoverableGTheme> findDiscoverableThemes() {
|
||||
return ClassSearcher.getInstances(DiscoverableGTheme.class);
|
||||
}
|
||||
|
||||
private static GTheme getThemeFromPreferences() {
|
||||
String themeId = Preferences.getProperty(THEME_PREFFERENCE_KEY, "Default", true);
|
||||
if (themeId.startsWith(FileGTheme.FILE_PREFIX)) {
|
||||
String filename = themeId.substring(FileGTheme.FILE_PREFIX.length());
|
||||
try {
|
||||
return new FileGTheme(new File(filename));
|
||||
}
|
||||
catch (IOException e) {
|
||||
Msg.showError(GTheme.class, null, "Can't Load Previous Theme",
|
||||
"Error loading theme file: " + filename);
|
||||
}
|
||||
}
|
||||
else if (themeId.startsWith(DiscoverableGTheme.CLASS_PREFIX)) {
|
||||
String className = themeId.substring(DiscoverableGTheme.CLASS_PREFIX.length());
|
||||
try {
|
||||
Class<?> forName = Class.forName(className);
|
||||
return (GTheme) forName.getDeclaredConstructor().newInstance();
|
||||
}
|
||||
catch (Exception e) {
|
||||
Msg.showError(GTheme.class, null, "Can't Load Previous Theme",
|
||||
"Can't find or instantiate class: " + className);
|
||||
}
|
||||
}
|
||||
return new DefaultTheme();
|
||||
}
|
||||
|
||||
public static GThemeValueMap getCoreDefaults() {
|
||||
GThemeValueMap map = new GThemeValueMap(ghidraCoreDefaults);
|
||||
map.load(javaDefaults);
|
||||
return map;
|
||||
}
|
||||
|
||||
public static GThemeValueMap getDarkDefaults() {
|
||||
GThemeValueMap map = new GThemeValueMap(ghidraCoreDefaults);
|
||||
map.load(darkDefaults);
|
||||
return map;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package docking.theme;
|
||||
|
||||
import ghidra.util.Msg;
|
||||
|
||||
public class IconValue extends ThemeValue<String> {
|
||||
static final String ICON_ID_PREFIX = "icon.";
|
||||
|
||||
public static final String LAST_RESORT_DEFAULT = "images/bomb.gif";
|
||||
|
||||
private static final String EXTERNAL_PREFIX = "[icon]";
|
||||
|
||||
public IconValue(String id, String refId, String iconPath) {
|
||||
super(id, refId, iconPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IconValue getReferredValue(GThemeValueMap values, String refId) {
|
||||
return values.getIcon(refId);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getUnresolvedReferenceValue(String id) {
|
||||
Msg.warn(this,
|
||||
"Could not resolve indirect icon path for" + id + ", using last resort default");
|
||||
return LAST_RESORT_DEFAULT;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getIdPrefix() {
|
||||
return ICON_ID_PREFIX;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toExternalId(String internalId) {
|
||||
if (internalId.startsWith(ICON_ID_PREFIX)) {
|
||||
return internalId;
|
||||
}
|
||||
return EXTERNAL_PREFIX + internalId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String fromExternalId(String externalId) {
|
||||
if (externalId.startsWith(EXTERNAL_PREFIX)) {
|
||||
return externalId.substring(EXTERNAL_PREFIX.length());
|
||||
}
|
||||
return externalId;
|
||||
}
|
||||
|
||||
public static boolean isIconKey(String key) {
|
||||
return key.startsWith(ICON_ID_PREFIX) || key.startsWith(EXTERNAL_PREFIX);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int compareValues(String v1, String v2) {
|
||||
return v1.compareTo(v2);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package docking.theme;
|
||||
|
||||
public interface Refreshable {
|
||||
public void refresh();
|
||||
}
|
|
@ -0,0 +1,176 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package docking.theme;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.JLabel;
|
||||
|
||||
import docking.widgets.table.*;
|
||||
import ghidra.docking.settings.Settings;
|
||||
import ghidra.framework.plugintool.ServiceProvider;
|
||||
import ghidra.framework.plugintool.ServiceProviderStub;
|
||||
import ghidra.util.ColorUtils;
|
||||
import ghidra.util.WebColors;
|
||||
import ghidra.util.table.column.AbstractGColumnRenderer;
|
||||
import ghidra.util.table.column.GColumnRenderer;
|
||||
|
||||
public class ThemeColorTableModel extends GDynamicColumnTableModel<ColorValue, Object> {
|
||||
private List<ColorValue> colors;
|
||||
|
||||
public ThemeColorTableModel(GTheme theme) {
|
||||
super(new ServiceProviderStub());
|
||||
colors = Gui.getAllValues().getColors();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Users";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ColorValue> getModelData() {
|
||||
return colors;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TableColumnDescriptor<ColorValue> createTableColumnDescriptor() {
|
||||
TableColumnDescriptor<ColorValue> descriptor = new TableColumnDescriptor<>();
|
||||
descriptor.addVisibleColumn(new IdColumn());
|
||||
descriptor.addVisibleColumn(new IsLafPropertyColumn());
|
||||
descriptor.addVisibleColumn(new ValueColumn("Current Color", Gui.getAllValues()));
|
||||
descriptor.addVisibleColumn(new ValueColumn("Core Defaults", Gui.getAllValues()));
|
||||
descriptor.addVisibleColumn(new ValueColumn("Dark Defaults", Gui.getDarkDefaults()));
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getDataSource() {
|
||||
return null;
|
||||
}
|
||||
|
||||
class IdColumn extends AbstractDynamicTableColumn<ColorValue, String, Object> {
|
||||
|
||||
@Override
|
||||
public String getColumnName() {
|
||||
return "Id";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue(ColorValue themeColor, Settings settings, Object data,
|
||||
ServiceProvider provider) throws IllegalArgumentException {
|
||||
return themeColor.getId();
|
||||
}
|
||||
}
|
||||
|
||||
class ValueColumn extends AbstractDynamicTableColumn<ColorValue, String, Object> {
|
||||
private ThemeColorRenderer renderer = new ThemeColorRenderer(Gui.getAllValues());
|
||||
private GThemeValueMap valueMap;
|
||||
private String name;
|
||||
|
||||
ValueColumn(String name, GThemeValueMap valueMap) {
|
||||
this.name = name;
|
||||
this.valueMap = valueMap;
|
||||
renderer = new ThemeColorRenderer(valueMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getColumnName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue(ColorValue themeColor, Settings settings, Object data,
|
||||
ServiceProvider provider) throws IllegalArgumentException {
|
||||
return themeColor.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public GColumnRenderer<String> getColumnRenderer() {
|
||||
return renderer;
|
||||
}
|
||||
|
||||
public Comparator<String> getComparator() {
|
||||
return (s1, s2) -> valueMap.getColor(s1).compareValue(valueMap.getColor(s2));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class IsLafPropertyColumn extends AbstractDynamicTableColumn<ColorValue, Boolean, Object> {
|
||||
|
||||
@Override
|
||||
public String getColumnName() {
|
||||
return "Is Laf";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean getValue(ColorValue themeColor, Settings settings, Object data,
|
||||
ServiceProvider provider) throws IllegalArgumentException {
|
||||
return Gui.isJavaDefinedColor(themeColor.getId());
|
||||
}
|
||||
}
|
||||
|
||||
private class ThemeColorRenderer extends AbstractGColumnRenderer<String> {
|
||||
|
||||
private GThemeValueMap valueMap;
|
||||
|
||||
public ThemeColorRenderer(GThemeValueMap valueMap) {
|
||||
this.valueMap = valueMap;
|
||||
setFont(new Font("Monospaced", Font.PLAIN, 12));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getTableCellRendererComponent(GTableCellRenderingData data) {
|
||||
|
||||
JLabel label = (JLabel) super.getTableCellRendererComponent(data);
|
||||
String id = (String) data.getValue();
|
||||
|
||||
ColorValue colorValue = valueMap.getColor(id);
|
||||
Color color;
|
||||
String text;
|
||||
if (colorValue != null) {
|
||||
color = colorValue.get(valueMap);
|
||||
if (colorValue.getReferenceId() != null) {
|
||||
text = colorValue.getReferenceId();
|
||||
}
|
||||
else {
|
||||
text = WebColors.toString(color, false);
|
||||
String name = WebColors.toWebColorName(color);
|
||||
if (name != null) {
|
||||
text += " [" + name + "]";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
color = GThemeDefaults.Colors.BACKGROUND;
|
||||
text = "<No Value>";
|
||||
}
|
||||
label.setText(text);
|
||||
label.setBackground(color);
|
||||
label.setForeground(ColorUtils.contrastForegroundColor(color));
|
||||
label.setOpaque(true);
|
||||
return label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFilterString(String id, Settings settings) {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package docking.theme;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import generic.jar.ResourceFile;
|
||||
import ghidra.framework.Application;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
public class ThemePropertiesLoader {
|
||||
GThemeValueMap defaults = new GThemeValueMap();
|
||||
GThemeValueMap darkDefaults = new GThemeValueMap();
|
||||
|
||||
ThemePropertiesLoader() {
|
||||
}
|
||||
|
||||
public void initialize() {
|
||||
List<ResourceFile> themeDefaultFiles =
|
||||
Application.findFilesByExtensionInApplication(".theme.properties");
|
||||
|
||||
for (ResourceFile resourceFile : themeDefaultFiles) {
|
||||
Msg.debug(this, "found theme file: " + resourceFile.getAbsolutePath());
|
||||
try {
|
||||
ThemePropertyFileReader reader = new ThemePropertyFileReader(resourceFile);
|
||||
defaults.load(reader.getDefaultValues());
|
||||
darkDefaults.load(reader.getDarkDefaultValues());
|
||||
}
|
||||
catch (IOException e) {
|
||||
Msg.error(this,
|
||||
"Error reading theme properties file: " + resourceFile.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public GThemeValueMap getDefaults() {
|
||||
return defaults;
|
||||
}
|
||||
|
||||
public GThemeValueMap getDarkDefaults() {
|
||||
return darkDefaults;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,267 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package docking.theme;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Font;
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
import org.apache.commons.collections4.map.HashedMap;
|
||||
|
||||
import generic.jar.ResourceFile;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.WebColors;
|
||||
|
||||
public class ThemePropertyFileReader {
|
||||
|
||||
private static final String NO_SECTION = "[No Section]";
|
||||
private static final String DEFAULTS = "[Defaults]";
|
||||
private static final String DARK_DEFAULTS = "[Dark Defaults]";
|
||||
|
||||
private GThemeValueMap defaults = new GThemeValueMap();
|
||||
private GThemeValueMap darkDefaults = new GThemeValueMap();
|
||||
private Map<String, List<String>> aliasMap = new HashedMap<>();
|
||||
private List<String> errors = new ArrayList<>();
|
||||
private String filePath;
|
||||
|
||||
public ThemePropertyFileReader(File file) throws IOException {
|
||||
filePath = file.getAbsolutePath();
|
||||
try (Reader reader = new FileReader(file)) {
|
||||
read(reader);
|
||||
}
|
||||
}
|
||||
|
||||
public ThemePropertyFileReader(ResourceFile file) throws IOException {
|
||||
filePath = file.getAbsolutePath();
|
||||
try (Reader reader = new InputStreamReader(file.getInputStream())) {
|
||||
read(reader);
|
||||
}
|
||||
}
|
||||
|
||||
ThemePropertyFileReader(String source, Reader reader) throws IOException {
|
||||
filePath = source;
|
||||
read(reader);
|
||||
}
|
||||
|
||||
public GThemeValueMap getDefaultValues() {
|
||||
return defaults;
|
||||
}
|
||||
|
||||
public GThemeValueMap getDarkDefaultValues() {
|
||||
return darkDefaults;
|
||||
}
|
||||
|
||||
public Map<String, List<String>> getAliases() {
|
||||
return aliasMap;
|
||||
}
|
||||
|
||||
public List<String> getErrors() {
|
||||
return errors;
|
||||
}
|
||||
|
||||
private void read(Reader reader) throws IOException {
|
||||
List<Section> sections = readSections(new LineNumberReader(reader));
|
||||
for (Section section : sections) {
|
||||
switch (section.getName()) {
|
||||
case NO_SECTION:
|
||||
processNoSection(section);
|
||||
break;
|
||||
case DEFAULTS:
|
||||
processValues(defaults, section);
|
||||
break;
|
||||
case DARK_DEFAULTS:
|
||||
processValues(darkDefaults, section);
|
||||
break;
|
||||
default:
|
||||
error(section.getLineNumber(),
|
||||
"Encounded unknown theme file section: " + section.getName());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected void processNoSection(Section section) throws IOException {
|
||||
if (!section.isEmpty()) {
|
||||
error(0, "Theme properties file has values defined outside of a defined section");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void processValues(GThemeValueMap valueMap, Section section) {
|
||||
for (String key : section.getKeys()) {
|
||||
String value = section.getValue(key);
|
||||
int lineNumber = section.getLineNumber(key);
|
||||
if (ColorValue.isColorKey(key)) {
|
||||
valueMap.addColor(parseColorProperty(key, value, lineNumber));
|
||||
}
|
||||
else if (FontValue.isFontKey(key)) {
|
||||
valueMap.addFont(parseFontProperty(key, value, lineNumber));
|
||||
}
|
||||
else if (IconValue.isIconKey(key)) {
|
||||
valueMap.addIconPath(parseIconProperty(key, value));
|
||||
}
|
||||
else {
|
||||
error(lineNumber, "Can't process property: " + key + " = " + value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private IconValue parseIconProperty(String key, String value) {
|
||||
if (IconValue.isIconKey(value)) {
|
||||
return new IconValue(key, value, null);
|
||||
}
|
||||
return new IconValue(key, null, value);
|
||||
}
|
||||
|
||||
private FontValue parseFontProperty(String key, String value, int lineNumber) {
|
||||
if (FontValue.isFontKey(value)) {
|
||||
return new FontValue(key, value);
|
||||
}
|
||||
Font font = Font.decode(value);
|
||||
if (font == null) {
|
||||
error(lineNumber, "Could not parse Color: " + value);
|
||||
}
|
||||
return font == null ? null : new FontValue(key, font);
|
||||
}
|
||||
|
||||
private ColorValue parseColorProperty(String key, String value, int lineNumber) {
|
||||
if (ColorValue.isColorKey(value)) {
|
||||
return new ColorValue(key, value);
|
||||
}
|
||||
Color color = WebColors.getColor(value);
|
||||
if (color == null) {
|
||||
error(lineNumber, "Could not parse Color: " + value);
|
||||
}
|
||||
return color == null ? null : new ColorValue(key, color);
|
||||
}
|
||||
|
||||
private List<Section> readSections(LineNumberReader reader) throws IOException {
|
||||
|
||||
List<Section> sections = new ArrayList<>();
|
||||
Section currentSection = new Section(NO_SECTION, 0);
|
||||
sections.add(currentSection);
|
||||
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
line = removeComments(line);
|
||||
|
||||
if (line.isBlank()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isSectionHeader(line)) {
|
||||
currentSection = new Section(line, reader.getLineNumber());
|
||||
sections.add(currentSection);
|
||||
}
|
||||
else {
|
||||
currentSection.add(line, reader.getLineNumber());
|
||||
}
|
||||
}
|
||||
|
||||
return sections;
|
||||
}
|
||||
|
||||
private String removeComments(String line) {
|
||||
// remove any trailing comment on line
|
||||
int commentIndex = line.indexOf("//");
|
||||
if (commentIndex >= 0) {
|
||||
line = line.substring(0, commentIndex);
|
||||
}
|
||||
line = line.trim();
|
||||
|
||||
// clear line if entire line is comment
|
||||
if (line.startsWith("#")) {
|
||||
return "";
|
||||
}
|
||||
return line;
|
||||
}
|
||||
|
||||
private boolean isSectionHeader(String line) {
|
||||
return line.startsWith("[") && line.endsWith("]");
|
||||
}
|
||||
|
||||
protected void error(int lineNumber, String message) {
|
||||
String msg =
|
||||
"Error parsing file \"" + filePath + "\" at line: " + lineNumber + ", " + message;
|
||||
errors.add(msg);
|
||||
Msg.out(msg);
|
||||
}
|
||||
|
||||
protected class Section {
|
||||
|
||||
private String name;
|
||||
Map<String, String> properties = new HashMap<>();
|
||||
Map<String, Integer> lineNumbers = new HashMap<>();
|
||||
private int startLineNumber;
|
||||
|
||||
public Section(String sectionName, int lineNumber) {
|
||||
this.name = sectionName;
|
||||
this.startLineNumber = lineNumber;
|
||||
}
|
||||
|
||||
public void remove(String key) {
|
||||
properties.remove(key);
|
||||
}
|
||||
|
||||
public String getValue(String key) {
|
||||
return properties.get(key);
|
||||
}
|
||||
|
||||
public Set<String> getKeys() {
|
||||
return properties.keySet();
|
||||
}
|
||||
|
||||
public int getLineNumber(String key) {
|
||||
return lineNumbers.get(key);
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return properties.isEmpty();
|
||||
}
|
||||
|
||||
public int getLineNumber() {
|
||||
return startLineNumber;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void add(String line, int lineNumber) {
|
||||
int splitIndex = line.indexOf('=');
|
||||
if (splitIndex < 0) {
|
||||
error(lineNumber, "Missing required \"=\" for propery line: \"" + line + "\"");
|
||||
return;
|
||||
}
|
||||
String key = line.substring(0, splitIndex).trim();
|
||||
String value = line.substring(splitIndex + 1, line.length()).trim();
|
||||
if (key.isBlank()) {
|
||||
error(lineNumber, "Missing key for propery line: \"" + line + "\"");
|
||||
return;
|
||||
}
|
||||
if (key.isBlank()) {
|
||||
error(lineNumber, "Missing value for propery line: \"" + line + "\"");
|
||||
return;
|
||||
}
|
||||
properties.put(key, value);
|
||||
lineNumbers.put(key, lineNumber);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package docking.theme;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
public class ThemeReader extends ThemePropertyFileReader {
|
||||
|
||||
private Section themeSection;
|
||||
private String themeName;
|
||||
private String lookAndFeelName;
|
||||
private boolean isDark;
|
||||
|
||||
public ThemeReader(File file) throws IOException {
|
||||
super(file);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processNoSection(Section section) throws IOException {
|
||||
themeSection = section;
|
||||
themeName = section.getValue(GTheme.THEME_NAME_KEY);
|
||||
lookAndFeelName = section.getValue(GTheme.THEME_LOOK_AND_FEEL_KEY);
|
||||
if (themeName == null) {
|
||||
throw new IOException("Missing theme name and/or lookAndFeel name!");
|
||||
}
|
||||
if (lookAndFeelName == null) {
|
||||
error(section.getLineNumber(), "Invalid theme - missing theme name!");
|
||||
return;
|
||||
}
|
||||
isDark = Boolean.parseBoolean(section.getValue(GTheme.THEME_IS_DARK_KEY));
|
||||
}
|
||||
|
||||
void loadValues(GTheme theme) {
|
||||
|
||||
// processValues expects only colors, fonts, and icons
|
||||
themeSection.remove(GTheme.THEME_NAME_KEY);
|
||||
themeSection.remove(GTheme.THEME_LOOK_AND_FEEL_KEY);
|
||||
themeSection.remove(GTheme.THEME_IS_DARK_KEY);
|
||||
|
||||
processValues(theme, themeSection);
|
||||
}
|
||||
|
||||
public String getThemeName() {
|
||||
return themeName;
|
||||
}
|
||||
|
||||
public String getLookAndFeelName() {
|
||||
return lookAndFeelName;
|
||||
}
|
||||
|
||||
public boolean isDark() {
|
||||
return isDark;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,132 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package docking.theme;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.util.Msg;
|
||||
|
||||
// TODO doc why 'cachedValue' is lazy loaded
|
||||
public abstract class ThemeValue<T> implements Comparable<ThemeValue<T>> {
|
||||
private final String id;
|
||||
private final T value;
|
||||
private final String refId;
|
||||
private T cachedValue;
|
||||
|
||||
protected ThemeValue(String id, String refId, T value) {
|
||||
this.id = fromExternalId(id);
|
||||
this.refId = (refId == null) ? null : fromExternalId(refId);
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
protected abstract String getIdPrefix();
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getReferenceId() {
|
||||
return refId;
|
||||
}
|
||||
|
||||
public T getRawValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
T get(GThemeValueMap preferredValues) {
|
||||
if (cachedValue == null) {
|
||||
cachedValue = doGetValue(preferredValues);
|
||||
}
|
||||
return cachedValue;
|
||||
}
|
||||
|
||||
private T doGetValue(GThemeValueMap values) {
|
||||
ThemeValue<T> result = this;
|
||||
Set<String> visitedKeys = new HashSet<>();
|
||||
visitedKeys.add(id); // seed with my id, we don't want to see that key again
|
||||
|
||||
// loop resolving indirect references
|
||||
while (result != null) {
|
||||
if (result.value != null) {
|
||||
return result.value;
|
||||
}
|
||||
if (visitedKeys.contains(result.refId)) {
|
||||
Msg.warn(this, "Theme value reference loop detected for key: " + id);
|
||||
return getUnresolvedReferenceValue(id);
|
||||
}
|
||||
result = getReferredValue(values, result.refId);
|
||||
}
|
||||
return getUnresolvedReferenceValue(id);
|
||||
}
|
||||
|
||||
abstract protected T getUnresolvedReferenceValue(String theId);
|
||||
|
||||
abstract public String toExternalId(String internalId);
|
||||
|
||||
abstract public String fromExternalId(String externalId);
|
||||
|
||||
abstract protected ThemeValue<T> getReferredValue(GThemeValueMap preferredValues,
|
||||
String theRefId);
|
||||
|
||||
@Override
|
||||
public int compareTo(ThemeValue<T> o) {
|
||||
return id.compareTo(o.id);
|
||||
}
|
||||
|
||||
public int compareValue(ThemeValue<T> o) {
|
||||
if (refId != null) {
|
||||
return o.refId != null ? refId.compareTo(o.refId) : -1;
|
||||
}
|
||||
if (o.refId != null) {
|
||||
return 1;
|
||||
}
|
||||
return compareValues(value, o.value);
|
||||
}
|
||||
|
||||
protected abstract int compareValues(T v1, T v2);
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id, refId, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
ThemeValue<?> other = (ThemeValue<?>) obj;
|
||||
|
||||
return Objects.equals(id, other.id) && Objects.equals(refId, other.refId) &&
|
||||
Objects.equals(value, other.value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String name = getClass().getSimpleName();
|
||||
if (refId == null) {
|
||||
return name + " (" + id + ", " + value + ")";
|
||||
}
|
||||
return name + " (" + id + ", " + refId + ")";
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package docking.theme.builtin;
|
||||
|
||||
import docking.theme.DiscoverableGTheme;
|
||||
import ghidra.docking.util.LookAndFeelUtils;
|
||||
|
||||
public class CDEMotifTheme extends DiscoverableGTheme {
|
||||
|
||||
public CDEMotifTheme() {
|
||||
super("CDE/Motif", LookAndFeelUtils.MOTIF_LOOK_AND_FEEL);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package docking.theme.builtin;
|
||||
|
||||
import docking.theme.DiscoverableGTheme;
|
||||
import ghidra.docking.util.LookAndFeelUtils;
|
||||
|
||||
public class FlatDarkTheme extends DiscoverableGTheme {
|
||||
public FlatDarkTheme() {
|
||||
super("Dark Flat Theme", LookAndFeelUtils.FLAT_DARK_LOOK_AND_FEEL, true);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package docking.theme.builtin;
|
||||
|
||||
import docking.theme.DiscoverableGTheme;
|
||||
import ghidra.docking.util.LookAndFeelUtils;
|
||||
|
||||
public class FlatLightTheme extends DiscoverableGTheme {
|
||||
|
||||
public FlatLightTheme() {
|
||||
super("Flat", LookAndFeelUtils.FLAT_LIGHT_LOOK_AND_FEEL);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package docking.theme.builtin;
|
||||
|
||||
import docking.theme.DiscoverableGTheme;
|
||||
import ghidra.docking.util.LookAndFeelUtils;
|
||||
|
||||
public class GdkTheme extends DiscoverableGTheme {
|
||||
|
||||
public GdkTheme() {
|
||||
super("GDK+", LookAndFeelUtils.GDK_LOOK_AND_FEEL);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package docking.theme.builtin;
|
||||
|
||||
import docking.theme.DiscoverableGTheme;
|
||||
import ghidra.docking.util.LookAndFeelUtils;
|
||||
|
||||
public class MetalTheme extends DiscoverableGTheme {
|
||||
|
||||
public MetalTheme() {
|
||||
super("Metal", LookAndFeelUtils.METAL_LOOK_AND_FEEL);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package docking.theme.builtin;
|
||||
|
||||
import docking.theme.DiscoverableGTheme;
|
||||
|
||||
public class NimbusTheme extends DiscoverableGTheme {
|
||||
|
||||
public NimbusTheme() {
|
||||
super("Nimbus", "Nimbus");
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package docking.theme.builtin;
|
||||
|
||||
import docking.theme.DiscoverableGTheme;
|
||||
import ghidra.docking.util.LookAndFeelUtils;
|
||||
|
||||
public class WindowsClassicTheme extends DiscoverableGTheme {
|
||||
|
||||
public WindowsClassicTheme() {
|
||||
super("Windows Classic", LookAndFeelUtils.WINDOWS_CLASSIC);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package docking.theme.builtin;
|
||||
|
||||
import docking.theme.DiscoverableGTheme;
|
||||
import ghidra.docking.util.LookAndFeelUtils;
|
||||
|
||||
public class WindowsTheme extends DiscoverableGTheme {
|
||||
|
||||
public WindowsTheme() {
|
||||
super("Windows", LookAndFeelUtils.WINDOWS);
|
||||
}
|
||||
}
|
|
@ -21,6 +21,7 @@ import javax.swing.BorderFactory;
|
|||
import javax.swing.JComponent;
|
||||
import javax.swing.border.Border;
|
||||
|
||||
import docking.theme.GColor;
|
||||
import docking.widgets.label.GDHtmlLabel;
|
||||
|
||||
/**
|
||||
|
@ -31,8 +32,8 @@ import docking.widgets.label.GDHtmlLabel;
|
|||
*
|
||||
*/
|
||||
public abstract class AbstractGCellRenderer extends GDHtmlLabel {
|
||||
|
||||
private static final Color ALTERNATE_BACKGROUND_COLOR = new Color(237, 243, 254);
|
||||
private static final Color BACKGROUND_COLOR = new GColor("color.bg.table.row");
|
||||
private static final Color ALT_BACKGROUND_COLOR = new GColor("color.bg.table.row.alt");
|
||||
|
||||
/** Allows the user to disable alternating row colors on JLists and JTables */
|
||||
private static final String DISABLE_ALTERNATING_ROW_COLORS_PROPERTY =
|
||||
|
@ -156,7 +157,7 @@ public abstract class AbstractGCellRenderer extends GDHtmlLabel {
|
|||
}
|
||||
|
||||
protected Color getDefaultBackgroundColor() {
|
||||
return Color.WHITE;
|
||||
return BACKGROUND_COLOR;
|
||||
}
|
||||
|
||||
protected Color getBackgroundColorForRow(int row) {
|
||||
|
@ -164,7 +165,7 @@ public abstract class AbstractGCellRenderer extends GDHtmlLabel {
|
|||
if ((row & 1) == 1) {
|
||||
return getDefaultBackgroundColor();
|
||||
}
|
||||
return ALTERNATE_BACKGROUND_COLOR;
|
||||
return ALT_BACKGROUND_COLOR;
|
||||
}
|
||||
|
||||
// ==================================================================================================
|
||||
|
|
|
@ -24,7 +24,7 @@ import javax.swing.border.EmptyBorder;
|
|||
import javax.swing.event.ChangeEvent;
|
||||
import javax.swing.event.ChangeListener;
|
||||
|
||||
import ghidra.docking.util.DockingWindowsLookAndFeelUtils;
|
||||
import ghidra.docking.util.LookAndFeelUtils;
|
||||
import resources.ResourceManager;
|
||||
|
||||
/**
|
||||
|
@ -123,7 +123,7 @@ public class EmptyBorderButton extends JButton {
|
|||
|
||||
// Mac OSX LNF doesn't give us rollover callbacks, so we have to add a mouse listener to
|
||||
// do the work
|
||||
if (DockingWindowsLookAndFeelUtils.isUsingAquaUI(getUI())) {
|
||||
if (LookAndFeelUtils.isUsingAquaUI(getUI())) {
|
||||
addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseEntered(MouseEvent e) {
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
*/
|
||||
package docking.widgets.fieldpanel;
|
||||
|
||||
import static docking.widgets.EventTrigger.INTERNAL_ONLY;
|
||||
import static docking.widgets.EventTrigger.*;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
|
@ -29,6 +29,7 @@ import javax.swing.event.ChangeEvent;
|
|||
import javax.swing.event.ChangeListener;
|
||||
|
||||
import docking.DockingUtils;
|
||||
import docking.theme.GColor;
|
||||
import docking.util.GraphicsUtils;
|
||||
import docking.widgets.EventTrigger;
|
||||
import docking.widgets.fieldpanel.field.Field;
|
||||
|
@ -51,7 +52,7 @@ public class FieldPanel extends JPanel
|
|||
private boolean inFocus;
|
||||
|
||||
protected BackgroundColorModel backgroundColorModel =
|
||||
new DefaultBackgroundColorModel(Color.WHITE);
|
||||
new DefaultBackgroundColorModel(new GColor("color.bg.fieldpanel"));
|
||||
protected PaintContext paintContext = new PaintContext();
|
||||
|
||||
private AnchoredLayoutHandler layoutHandler;
|
||||
|
@ -415,7 +416,6 @@ public class FieldPanel extends JPanel
|
|||
*/
|
||||
public void setBackgroundColor(Color c) {
|
||||
backgroundColorModel.setDefaultBackgroundColor(c);
|
||||
paintContext.setDefaultBackgroundColor(c);
|
||||
}
|
||||
|
||||
public Color getBackgroundColor(BigInteger index) {
|
||||
|
@ -1238,8 +1238,7 @@ public class FieldPanel extends JPanel
|
|||
if (layout == null) {
|
||||
return null;
|
||||
}
|
||||
Rectangle r =
|
||||
layout.getCursorRect(location.fieldNum, location.row, location.col);
|
||||
Rectangle r = layout.getCursorRect(location.fieldNum, location.row, location.col);
|
||||
return r.getLocation();
|
||||
}
|
||||
|
||||
|
|
|
@ -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,12 +17,13 @@ package docking.widgets.fieldpanel.internal;
|
|||
|
||||
import java.awt.Color;
|
||||
|
||||
import docking.theme.GColor;
|
||||
|
||||
/**
|
||||
* Miscellaneous information needed by fields to paint.
|
||||
*/
|
||||
public class PaintContext {
|
||||
|
||||
private Color defaultBackground;
|
||||
private Color background;
|
||||
private Color foreground;
|
||||
private Color selectionColor;
|
||||
|
@ -43,20 +43,18 @@ public class PaintContext {
|
|||
* Create a new PaintContext with default color values.
|
||||
*/
|
||||
public PaintContext() {
|
||||
defaultBackground = Color.white;
|
||||
background = Color.white;
|
||||
foreground = Color.black;
|
||||
selectionColor = new Color(180, 255, 180);
|
||||
highlightColor = new Color(255, 255, 150);
|
||||
selectedHighlightColor = Color.green;
|
||||
focusedCursorColor = Color.RED;
|
||||
background = new GColor("color.bg.fieldpanel");
|
||||
foreground = new GColor("color.fg.fieldpanel");
|
||||
selectionColor = new GColor("color.bg.fieldpanel.selection");
|
||||
highlightColor = new GColor("color.bg.fieldpanel.highlight");
|
||||
selectedHighlightColor = new GColor("color.bg.fieldpanel.selection-highlight");
|
||||
focusedCursorColor = new GColor("color.cursor.focused");
|
||||
notFocusedCursorColor = new GColor("color.cursor.unfocused");
|
||||
cursorColor = focusedCursorColor;
|
||||
invisibleCursorColor = new Color(255, 0, 0, 1);
|
||||
notFocusedCursorColor = Color.PINK;
|
||||
}
|
||||
|
||||
public PaintContext(PaintContext other) {
|
||||
defaultBackground = other.defaultBackground;
|
||||
background = other.background;
|
||||
foreground = other.foreground;
|
||||
selectionColor = other.selectionColor;
|
||||
|
@ -69,16 +67,9 @@ public class PaintContext {
|
|||
printColor = other.printColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current default background color setting that is used when
|
||||
* there is no special background color or highlight or selection.
|
||||
*/
|
||||
public final Color getDefaultBackground() {
|
||||
return defaultBackground;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current background color setting.
|
||||
* @return the current background color setting.
|
||||
*/
|
||||
public final Color getBackground() {
|
||||
return background;
|
||||
|
@ -86,6 +77,7 @@ public class PaintContext {
|
|||
|
||||
/**
|
||||
* Returns the current foreground color setting.
|
||||
* @return the current foreground color setting.
|
||||
*/
|
||||
public final Color getForeground() {
|
||||
return foreground;
|
||||
|
@ -93,27 +85,31 @@ public class PaintContext {
|
|||
|
||||
/**
|
||||
* Returns the current selection color setting.
|
||||
* @return the current selection color setting.
|
||||
*/
|
||||
public final Color getSelectionColor() {
|
||||
return selectionColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current selection color setting.
|
||||
* Returns the current highlight color setting.
|
||||
* @return the current highlight color setting.
|
||||
*/
|
||||
public final Color getHighlightColor() {
|
||||
return highlightColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current selection color setting.
|
||||
* Returns the current selected highlight color setting.
|
||||
* @return the current selected highlight color setting.
|
||||
*/
|
||||
public final Color getSelectedHighlightColor() {
|
||||
return selectedHighlightColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current cursor color setting.
|
||||
* Returns the current cursor color.
|
||||
* @return the current cursor color.
|
||||
*/
|
||||
public final Color getCursorColor() {
|
||||
return cursorColor;
|
||||
|
@ -133,17 +129,6 @@ public class PaintContext {
|
|||
adjustSelectedHighlightColor();
|
||||
}
|
||||
|
||||
public void setDefaultBackgroundColor(Color c) {
|
||||
defaultBackground = c;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the current background color matches the default background color.
|
||||
*/
|
||||
public final boolean isDefaultBackground() {
|
||||
return defaultBackground.equals(background);
|
||||
}
|
||||
|
||||
private void adjustSelectedHighlightColor() {
|
||||
int red = (selectionColor.getRed() + highlightColor.getRed()) / 2;
|
||||
int green = (selectionColor.getGreen() + highlightColor.getGreen()) / 2;
|
||||
|
|
|
@ -15,23 +15,23 @@
|
|||
*/
|
||||
package docking.widgets.filechooser;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.CellEditorListener;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
import javax.swing.filechooser.FileSystemView;
|
||||
|
||||
import docking.*;
|
||||
import docking.theme.GColor;
|
||||
import docking.widgets.*;
|
||||
import docking.widgets.combobox.GComboBox;
|
||||
import docking.widgets.label.GDLabel;
|
||||
|
@ -73,8 +73,8 @@ import util.HistoryList;
|
|||
public class GhidraFileChooser extends DialogComponentProvider implements FileFilter {
|
||||
|
||||
static final String UP_BUTTON_NAME = "UP_BUTTON";
|
||||
private static final Color FOREROUND_COLOR = Color.BLACK;
|
||||
private static final Color BACKGROUND_COLOR = Color.WHITE;
|
||||
private static final Color FOREROUND_COLOR = new GColor("color.fg.filechooser");
|
||||
private static final Color BACKGROUND_COLOR = new GColor("color.bg.filechooser");
|
||||
static final String PREFERENCES_PREFIX = "G_FILE_CHOOSER";
|
||||
private static final String WIDTH_PREFERENCE_PREFIX = PREFERENCES_PREFIX + ".WIDTH.";
|
||||
private static final String HEIGHT_PREFERENCE_PREFIX = PREFERENCES_PREFIX + ".HEIGHT.";
|
||||
|
|
|
@ -24,6 +24,7 @@ import javax.swing.*;
|
|||
import javax.swing.border.*;
|
||||
import javax.swing.table.*;
|
||||
|
||||
import docking.theme.GColor;
|
||||
import docking.widgets.label.GDLabel;
|
||||
import resources.*;
|
||||
import resources.icons.EmptyIcon;
|
||||
|
@ -37,11 +38,17 @@ import resources.icons.TranslateIcon;
|
|||
*/
|
||||
public class GTableHeaderRenderer extends JPanel implements TableCellRenderer {
|
||||
private static final int PADDING_FOR_COLUMN_NUMBER = 10;
|
||||
//@formatter:off
|
||||
public static final Color GRADIENT_START_COLOR = new GColor("color.bg.tableheader.gradient.start");
|
||||
public static final Color GRADIENT_END_COLOR = new GColor("color.bg.tableheader.gradient.end");
|
||||
public static final Color GRADIENT_START_PRIMARY_COLOR = new GColor("color.bg.tableheader.gradient.start.primary");
|
||||
public static final Color GRADIENT_END_PRIMARY_COLOR= new GColor("color.bg.tableheader.gradient.end.primary");
|
||||
//@formatter:on
|
||||
|
||||
private static final Color PRIMARY_SORT_GRADIENT_START = new Color(205, 227, 244);
|
||||
private static final Color PRIMARY_SORT_GRADIENT_END = new Color(126, 186, 233);
|
||||
private static final Color DEFAULT_GRADIENT_START = Color.WHITE;
|
||||
private static final Color DEFAULT_GRADIENT_END = new Color(215, 215, 215);
|
||||
// static {
|
||||
// Gui.registerAltColor(GThemeDefaults.DARK, GRADIENT_START_ID, new Color(20, 20, 20));
|
||||
// Gui.registerAltColor(GThemeDefaults.DARK, GRADIENT_END_ID, new Color(40, 40, 40));
|
||||
// }
|
||||
|
||||
private static final Icon UP_ICON =
|
||||
ResourceManager.getScaledIcon(Icons.SORT_ASCENDING_ICON, 14, 14);
|
||||
|
@ -219,12 +226,12 @@ public class GTableHeaderRenderer extends JPanel implements TableCellRenderer {
|
|||
}
|
||||
|
||||
protected Paint getBackgroundPaint() {
|
||||
if (isPaintingPrimarySortColumn) {
|
||||
return new GradientPaint(0, 0, PRIMARY_SORT_GRADIENT_START, 0, getHeight() - 11,
|
||||
PRIMARY_SORT_GRADIENT_END, true);
|
||||
}
|
||||
return new GradientPaint(0, 0, DEFAULT_GRADIENT_START, 0, getHeight() - 11,
|
||||
DEFAULT_GRADIENT_END, true);
|
||||
Color startColor =
|
||||
isPaintingPrimarySortColumn ? GRADIENT_START_PRIMARY_COLOR : GRADIENT_START_COLOR;
|
||||
Color endColor =
|
||||
isPaintingPrimarySortColumn ? GRADIENT_END_PRIMARY_COLOR : GRADIENT_END_COLOR;
|
||||
|
||||
return new GradientPaint(0, 0, startColor, 0, getHeight() - 11, endColor, true);
|
||||
}
|
||||
|
||||
private void updateHelpIcon(JTable table, int currentColumnIndex, Icon icon) {
|
||||
|
|
|
@ -22,6 +22,8 @@ import javax.swing.JTextField;
|
|||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
|
||||
import docking.theme.GColor;
|
||||
|
||||
/**
|
||||
* Simple text field that shows a text hint when the field is empty.
|
||||
*
|
||||
|
@ -39,8 +41,8 @@ public class HintTextField extends JTextField {
|
|||
// some indication of what the field should contain.
|
||||
private String hint;
|
||||
|
||||
private Color INVALID_COLOR = new Color(255, 225, 225);
|
||||
private Color VALID_COLOR = Color.WHITE;
|
||||
private Color VALID_COLOR = new GColor("color.bg.textfield.hint.valid");
|
||||
private Color INVALID_COLOR = new GColor("color.bg.textfield.hint.invalid");
|
||||
private Color defaultBackgroundColor;
|
||||
|
||||
/**
|
||||
|
@ -126,8 +128,7 @@ public class HintTextField extends JTextField {
|
|||
|
||||
Graphics2D g2 = (Graphics2D) g;
|
||||
g2.setColor(Color.LIGHT_GRAY);
|
||||
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
|
||||
RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
|
||||
Dimension size = getSize();
|
||||
Insets insets = getInsets();
|
||||
|
@ -179,7 +180,7 @@ public class HintTextField extends JTextField {
|
|||
*/
|
||||
private void setAttributes() {
|
||||
setFont(getFont().deriveFont(Font.PLAIN));
|
||||
setForeground(Color.BLACK);
|
||||
setForeground(new GColor("color.fg.textfield.hint"));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -15,16 +15,15 @@
|
|||
*/
|
||||
package ghidra.docking.util;
|
||||
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Font;
|
||||
import java.awt.*;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.UIManager.LookAndFeelInfo;
|
||||
import javax.swing.plaf.ComponentUI;
|
||||
|
||||
import ghidra.docking.util.painting.GRepaintManager;
|
||||
import ghidra.framework.OperatingSystem;
|
||||
import ghidra.framework.Platform;
|
||||
import ghidra.framework.preferences.Preferences;
|
||||
|
@ -33,56 +32,50 @@ import ghidra.util.*;
|
|||
/**
|
||||
* A utility class to manage LookAndFeel (LaF) settings.
|
||||
*/
|
||||
public class DockingWindowsLookAndFeelUtils {
|
||||
public class LookAndFeelUtils {
|
||||
|
||||
/**
|
||||
* Preference name for look and feel for the application.
|
||||
* Default Look and feel for the current platform.
|
||||
*/
|
||||
public final static String LAST_LOOK_AND_FEEL_KEY = "LastLookAndFeel";
|
||||
|
||||
/**
|
||||
* Preference name for whether to use inverted colors.
|
||||
*/
|
||||
public final static String USE_INVERTED_COLORS_KEY = "LookAndFeel.UseInvertedColors";
|
||||
public final static String SYSTEM_LOOK_AND_FEEL = "System";
|
||||
|
||||
/**
|
||||
* Metal is the non-system, generic Java Look and Feel.
|
||||
*/
|
||||
public final static String METAL_LOOK_AND_FEEL = "Metal";
|
||||
|
||||
/**
|
||||
* Default Look and feel for the current platform.
|
||||
*/
|
||||
private final static String SYSTEM_LOOK_AND_FEEL = "System";
|
||||
|
||||
/**
|
||||
* The most stable Linux LaF, based on anecdotal observation.
|
||||
*/
|
||||
private static final String NIMBUS_LOOK_AND_FEEL = "Nimbus";
|
||||
public static final String NIMBUS_LOOK_AND_FEEL = "Nimbus";
|
||||
|
||||
public static final String GDK_LOOK_AND_FEEL = "GTK+";
|
||||
|
||||
/**
|
||||
* The Motif LaF name.
|
||||
*/
|
||||
private static final String MOTIF_LOOK_AND_FEEL = "CDE/Motif";
|
||||
public static final String MOTIF_LOOK_AND_FEEL = "CDE/Motif";
|
||||
/**
|
||||
* The flatlaf implementation of light mode.
|
||||
*/
|
||||
public static final String FLAT_LIGHT_LOOK_AND_FEEL = "Flat Light";
|
||||
/**
|
||||
* The Flatlaf implementation of dark mode.
|
||||
*/
|
||||
public static final String FLAT_DARK_LOOK_AND_FEEL = "Flat Dark";
|
||||
public static final String WINDOWS = "Windows";
|
||||
public static final String WINDOWS_CLASSIC = "Windows Classic";
|
||||
|
||||
private static RepaintManager defaultSwingRepaintManager = null;
|
||||
public static final String SYSTEM = "System";
|
||||
|
||||
private DockingWindowsLookAndFeelUtils() {
|
||||
private LookAndFeelUtils() {
|
||||
// utils class, cannot create
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads settings from {@link Preferences}.
|
||||
*/
|
||||
public static void loadFromPreferences() {
|
||||
|
||||
boolean useHistoricalValue = true;
|
||||
String laf = Preferences.getProperty(LAST_LOOK_AND_FEEL_KEY, getDefaultLookAndFeelName(),
|
||||
useHistoricalValue);
|
||||
setLookAndFeel(laf);
|
||||
|
||||
boolean useInvertedColors = getUseInvertedColorsPreference();
|
||||
setUseInvertedColors(useInvertedColors);
|
||||
public static void installGlobalOverrides() {
|
||||
|
||||
//
|
||||
// Users can change this via the SystemUtilities.FONT_SIZE_OVERRIDE_PROPERTY_NAME
|
||||
|
@ -94,18 +87,6 @@ public class DockingWindowsLookAndFeelUtils {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link Preferences} value for whether to use inverted colors when painting.
|
||||
* @return the {@link Preferences} value for whether to use inverted colors when painting.
|
||||
*/
|
||||
public static boolean getUseInvertedColorsPreference() {
|
||||
boolean useHistoricalValue = true;
|
||||
String useInvertedColorsString = Preferences.getProperty(USE_INVERTED_COLORS_KEY,
|
||||
Boolean.FALSE.toString(), useHistoricalValue);
|
||||
boolean useInvertedColors = Boolean.parseBoolean(useInvertedColorsString);
|
||||
return useInvertedColors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the currently installed LaF.
|
||||
* @return the currently installed LaF.
|
||||
|
@ -137,19 +118,45 @@ public class DockingWindowsLookAndFeelUtils {
|
|||
installPopupMenuSettingsOverride();
|
||||
}
|
||||
catch (Exception exc) {
|
||||
Msg.error(DockingWindowsLookAndFeelUtils.class,
|
||||
Msg.error(LookAndFeelUtils.class,
|
||||
"Error loading Look and Feel: " + exc, exc);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void dumpUIProperties() {
|
||||
List<String> colorKeys = getLookAndFeelIdsForType(Color.class);
|
||||
Collections.sort(colorKeys);
|
||||
for (String string : colorKeys) {
|
||||
Msg.debug(LookAndFeelUtils.class, string + "\t\t" + UIManager.get(string));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static List<String> getLookAndFeelIdsForType(Class<?> clazz) {
|
||||
UIDefaults defaults = UIManager.getDefaults();
|
||||
List<String> colorKeys = new ArrayList<>();
|
||||
for (Entry<?, ?> entry : defaults.entrySet()) {
|
||||
Object key = entry.getKey();
|
||||
if (key instanceof String) {
|
||||
Object value = entry.getValue();
|
||||
if (clazz.isInstance(value)) {
|
||||
colorKeys.add((String) key);
|
||||
}
|
||||
}
|
||||
}
|
||||
return colorKeys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all installed LaFs. This will vary by OS.
|
||||
* @return all installed LaFs.
|
||||
*/
|
||||
public static List<String> getLookAndFeelNames() {
|
||||
List<String> list = new ArrayList<>();
|
||||
list.add(DockingWindowsLookAndFeelUtils.SYSTEM_LOOK_AND_FEEL);
|
||||
list.add(LookAndFeelUtils.SYSTEM_LOOK_AND_FEEL);
|
||||
list.add(LookAndFeelUtils.FLAT_LIGHT_LOOK_AND_FEEL);
|
||||
list.add(LookAndFeelUtils.FLAT_DARK_LOOK_AND_FEEL);
|
||||
|
||||
LookAndFeelInfo[] installedLookAndFeels = UIManager.getInstalledLookAndFeels();
|
||||
for (LookAndFeelInfo info : installedLookAndFeels) {
|
||||
|
@ -171,6 +178,12 @@ public class DockingWindowsLookAndFeelUtils {
|
|||
if (lookAndFeelName.equalsIgnoreCase(SYSTEM_LOOK_AND_FEEL)) {
|
||||
return UIManager.getSystemLookAndFeelClassName();
|
||||
}
|
||||
else if (lookAndFeelName.equalsIgnoreCase(FLAT_LIGHT_LOOK_AND_FEEL)) {
|
||||
return "com.formdev.flatlaf.FlatLightLaf";
|
||||
}
|
||||
else if (lookAndFeelName.equalsIgnoreCase(FLAT_DARK_LOOK_AND_FEEL)) {
|
||||
return "com.formdev.flatlaf.FlatDarkLaf";
|
||||
}
|
||||
|
||||
LookAndFeelInfo[] installedLookAndFeels = UIManager.getInstalledLookAndFeels();
|
||||
for (LookAndFeelInfo info : installedLookAndFeels) {
|
||||
|
@ -180,26 +193,11 @@ public class DockingWindowsLookAndFeelUtils {
|
|||
}
|
||||
}
|
||||
|
||||
Msg.debug(DockingWindowsLookAndFeelUtils.class,
|
||||
Msg.debug(LookAndFeelUtils.class,
|
||||
"Unable to find requested Look and Feel: " + lookAndFeelName);
|
||||
return UIManager.getSystemLookAndFeelClassName();
|
||||
}
|
||||
|
||||
public static void setUseInvertedColors(boolean useInvertedColors) {
|
||||
SystemUtilities.runIfSwingOrPostSwingLater(() -> {
|
||||
|
||||
if (defaultSwingRepaintManager == null) {
|
||||
defaultSwingRepaintManager = RepaintManager.currentManager(null /*unused*/);
|
||||
}
|
||||
|
||||
RepaintManager rm = defaultSwingRepaintManager;
|
||||
if (useInvertedColors) {
|
||||
rm = new GRepaintManager();
|
||||
}
|
||||
RepaintManager.setCurrentManager(rm);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Fixes issues in the currently running look and feel.
|
||||
*/
|
||||
|
@ -340,8 +338,9 @@ public class DockingWindowsLookAndFeelUtils {
|
|||
|
||||
/**
|
||||
* Returns the name of the default LookAndFeel for the current OS.
|
||||
* @return the name
|
||||
*/
|
||||
private static String getDefaultLookAndFeelName() {
|
||||
public static String getDefaultLookAndFeelName() {
|
||||
OperatingSystem OS = Platform.CURRENT_PLATFORM.getOperatingSystem();
|
||||
if (OS == OperatingSystem.LINUX) {
|
||||
return NIMBUS_LOOK_AND_FEEL;
|
||||
|
@ -369,8 +368,4 @@ public class DockingWindowsLookAndFeelUtils {
|
|||
return NIMBUS_LOOK_AND_FEEL.equals(lookAndFeel.getName());
|
||||
}
|
||||
|
||||
public static boolean isUsingMotifUI() {
|
||||
LookAndFeel lookAndFeel = UIManager.getLookAndFeel();
|
||||
return MOTIF_LOOK_AND_FEEL.equals(lookAndFeel.getName());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package docking.theme;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Font;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import generic.test.AbstractGenericTest;
|
||||
|
||||
public class GThemeTest extends AbstractGenericTest {
|
||||
|
||||
private static final Font COURIER = new Font("Courier", Font.BOLD, 14);
|
||||
private static final Font DIALOG = new Font("Dialog", Font.PLAIN, 16);
|
||||
private static final Color COLOR_WITH_ALPHA = new Color(10, 20, 30, 40);
|
||||
private static final String ICON_PATH_1 = "images/arrow.png";
|
||||
private static final String ICON_PATH_2 = "images/disk.png";
|
||||
|
||||
private GTheme theme;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
theme = new DefaultTheme();
|
||||
new Font("Courier", Font.BOLD, 12);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetName() {
|
||||
assertEquals("Default", theme.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetColor() {
|
||||
theme.setColor("color.a.1", Color.BLUE);
|
||||
assertEquals(Color.BLUE, theme.getColor("color.a.1").get(null));
|
||||
theme.setColor("color.a.1", Color.RED);
|
||||
assertEquals(Color.RED, theme.getColor("color.a.1").get(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetFont() {
|
||||
theme.setFont("font.a.1", DIALOG);
|
||||
assertEquals(DIALOG, theme.getFont("font.a.1").get(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetIconPath() {
|
||||
theme.setIcon("icon.a.1", ICON_PATH_1);
|
||||
assertEquals(ICON_PATH_1, theme.getIcon("icon.a.1").get(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSavingLoadingTheme() throws IOException {
|
||||
theme = new GTheme("abc");
|
||||
theme.setColor("color.a.1", Color.RED);
|
||||
theme.setColor("color.a.2", Color.BLUE);
|
||||
theme.setColor("color.a.3", COLOR_WITH_ALPHA);
|
||||
theme.setColorRef("color.a.4", "color.a.1");
|
||||
theme.setColor("foo.bar", Color.GREEN);
|
||||
theme.setColorRef("foo.bar.xyz", "foo.bar");
|
||||
|
||||
theme.setFont("font.a.1", COURIER);
|
||||
theme.setFont("font.a.2", DIALOG);
|
||||
theme.setFontRef("font.a.3", "font.a.1");
|
||||
theme.setFont("x.y.z", COURIER);
|
||||
theme.setFontRef("x.y.z.1", "x.y.z");
|
||||
|
||||
theme.setIcon("icon.a.1", ICON_PATH_1);
|
||||
theme.setIcon("icon.a.2", ICON_PATH_2);
|
||||
theme.setIconRef("icon.a.3", "icon.a.1");
|
||||
theme.setIcon("t.u.v", ICON_PATH_1);
|
||||
theme.setIconRef("t.u.v.1", "t.u.v");
|
||||
|
||||
File file = createTempFile("themeTest.theme");
|
||||
|
||||
theme = theme.saveToFile(file); // saveToFile returns new theme instance
|
||||
|
||||
assertEquals("abc", theme.getName());
|
||||
assertEquals("System", theme.getLookAndFeelName());
|
||||
|
||||
assertEquals(Color.RED, theme.getColor("color.a.1").get(theme));
|
||||
assertEquals(Color.BLUE, theme.getColor("color.a.2").get(theme));
|
||||
assertEquals(COLOR_WITH_ALPHA, theme.getColor("color.a.3").get(theme));
|
||||
assertEquals("color.a.1", theme.getColor("color.a.4").getReferenceId());
|
||||
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(COURIER, theme.getFont("font.a.1").get(theme));
|
||||
assertEquals(DIALOG, theme.getFont("font.a.2").get(theme));
|
||||
assertEquals(COURIER, theme.getFont("x.y.z").get(theme));
|
||||
assertEquals(COURIER, theme.getFont("x.y.z.1").get(theme));
|
||||
|
||||
assertEquals(ICON_PATH_1, theme.getIcon("icon.a.1").get(theme));
|
||||
assertEquals(ICON_PATH_2, theme.getIcon("icon.a.2").get(theme));
|
||||
assertEquals(ICON_PATH_1, theme.getIcon("t.u.v").get(theme));
|
||||
assertEquals(ICON_PATH_1, theme.getIcon("t.u.v.1").get(theme));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package docking.theme;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.collections4.map.HashedMap;
|
||||
import org.junit.Before;
|
||||
|
||||
import docking.test.AbstractDockingTest;
|
||||
|
||||
public class GuiTest extends AbstractDockingTest {
|
||||
|
||||
private Map<String, List<String>> aliasMap = new HashedMap<>();
|
||||
private GThemeValueMap darkValues = new GThemeValueMap();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
Gui.setPropertiesLoader(new ThemePropertiesLoader() {
|
||||
@Override
|
||||
public void initialize() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public GThemeValueMap getDarkDefaults() {
|
||||
return darkValues;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// @Test
|
||||
// public void testRegisteredColorBeforeAndAfterGuiInit() {
|
||||
// Gui.registerColor("test.before", Color.RED);
|
||||
// Gui.initialize();
|
||||
// Gui.registerColor("test.after", Color.BLUE);
|
||||
//
|
||||
// assertEquals(Color.RED, Gui.getColor("test.before"));
|
||||
// assertEquals(Color.BLUE, Gui.getColor("test.after"));
|
||||
// }
|
||||
|
||||
// @Test
|
||||
// public void testThemeColorOverride() {
|
||||
// Gui.initialize();
|
||||
// String id = "color.test.bg";
|
||||
// Gui.registerColor(id, Color.RED);
|
||||
// assertEquals(Color.RED, Gui.getColor(id));
|
||||
//
|
||||
// GTheme theme = new GTheme("Test");
|
||||
// theme.setColor(id, Color.BLUE);
|
||||
// Gui.setTheme(theme);
|
||||
//
|
||||
// assertEquals(Color.BLUE, Gui.getColor(id));
|
||||
//
|
||||
// Gui.setTheme(new GTheme("Test2"));
|
||||
// assertEquals(Color.RED, Gui.getColor(id));
|
||||
//
|
||||
// }
|
||||
|
||||
// @Test
|
||||
// public void testDarkOverride() {
|
||||
// String id = "color.test.bg";
|
||||
// // simulate registered dark color from theme property file
|
||||
// darkValues.addColor(new ColorValue(id, Color.BLACK));
|
||||
//
|
||||
// Gui.registerColor(id, Color.RED);
|
||||
// Gui.initialize();
|
||||
//
|
||||
// assertEquals(Color.RED, Gui.getColor(id));
|
||||
//
|
||||
// GTheme theme = new GTheme("Dark Test", "System", true);
|
||||
// Gui.setTheme(theme);
|
||||
//
|
||||
// assertEquals(Color.BLACK, Gui.getColor(id));
|
||||
// }
|
||||
|
||||
// @Test
|
||||
// public void testAliasOverride() {
|
||||
// String id = "color.test.bg";
|
||||
// //simulate alias defined
|
||||
// List<String> aliases = Arrays.asList("Menu.background");
|
||||
// aliasMap.put(id, aliases);
|
||||
//
|
||||
// Gui.registerColor(id, Color.RED);
|
||||
// Gui.initialize();
|
||||
// Color menuColor = UIManager.getColor("Menu.background");
|
||||
// assertNotEquals(menuColor, Color.RED);
|
||||
// assertEquals(menuColor, Gui.getColor(id));
|
||||
// }
|
||||
|
||||
// private void assertEqual(Color a, GColor b) {
|
||||
// if (a.getRGB() != b.getRGB()) {
|
||||
// fail("Expected: " + a.toString() + " but was: " + b.toString());
|
||||
// }
|
||||
// }
|
||||
|
||||
}
|
|
@ -0,0 +1,167 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package docking.theme;
|
||||
|
||||
import static ghidra.util.WebColors.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Font;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public class ThemePropertyFileReaderTest {
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaults() throws IOException {
|
||||
//@formatter:off
|
||||
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
|
||||
" color.b.4 = 0xff000080", // half alpha red
|
||||
" color.b.5 = rgb(0,0,255)", // BLUE
|
||||
" color.b.6 = rgba(255,0,0,0.5)", // half alpha red
|
||||
" color.b.7 = color.b.1", // ref
|
||||
" font.a.8 = dialog-PLAIN-14",
|
||||
" font.a.9 = font.a.8",
|
||||
" icon.a.10 = foo.png",
|
||||
" icon.a.11 = icon.a.10",
|
||||
"")));
|
||||
//@formatter:on
|
||||
|
||||
Color halfAlphaRed = new Color(0x80ff0000, true);
|
||||
GThemeValueMap values = reader.getDefaultValues();
|
||||
assertEquals(11, values.size());
|
||||
|
||||
assertEquals(WHITE, getColorOrRef(values, "color.b.1"));
|
||||
assertEquals(RED, getColorOrRef(values, "color.b.2"));
|
||||
assertEquals(GREEN, getColorOrRef(values, "color.b.3"));
|
||||
assertEquals(halfAlphaRed, getColorOrRef(values, "color.b.4"));
|
||||
assertEquals(BLUE, getColorOrRef(values, "color.b.5"));
|
||||
assertEquals(halfAlphaRed, getColorOrRef(values, "color.b.6"));
|
||||
assertEquals("color.b.1", getColorOrRef(values, "color.b.7"));
|
||||
|
||||
assertEquals(new Font("dialog", Font.PLAIN, 14), getFontOrRef(values, "font.a.8"));
|
||||
assertEquals("font.a.8", getFontOrRef(values, "font.a.9"));
|
||||
|
||||
assertEquals("foo.png", getIconOrRef(values, "icon.a.10"));
|
||||
assertEquals("icon.a.10", getIconOrRef(values, "icon.a.11"));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDarkDefaults() throws IOException {
|
||||
//@formatter:off
|
||||
ThemePropertyFileReader reader = new ThemePropertyFileReader("test", new StringReader(String.join("\n",
|
||||
"[Dark Defaults]",
|
||||
" color.b.1 = white", // WHITE
|
||||
" color.b.2 = #ff0000", // RED
|
||||
" color.b.3 = 0x008000", // GREEN
|
||||
" color.b.4 = 0xff000080", // half alpha red
|
||||
" color.b.5 = rgb(0,0,255)", // BLUE
|
||||
" color.b.6 = rgba(255,0,0,0.5)", // half alpha red
|
||||
" color.b.7 = color.b.1", // ref
|
||||
"")));
|
||||
//@formatter:on
|
||||
|
||||
Color halfAlphaRed = new Color(0x80ff0000, true);
|
||||
GThemeValueMap values = reader.getDarkDefaultValues();
|
||||
assertEquals(7, values.size());
|
||||
|
||||
assertEquals(WHITE, getColorOrRef(values, "color.b.1"));
|
||||
assertEquals(RED, getColorOrRef(values, "color.b.2"));
|
||||
assertEquals(GREEN, getColorOrRef(values, "color.b.3"));
|
||||
assertEquals(halfAlphaRed, getColorOrRef(values, "color.b.4"));
|
||||
assertEquals(BLUE, getColorOrRef(values, "color.b.5"));
|
||||
assertEquals(halfAlphaRed, getColorOrRef(values, "color.b.6"));
|
||||
assertEquals("color.b.1", getColorOrRef(values, "color.b.7"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBothDefaultsAndDarkDefaultsInSameFile() throws IOException {
|
||||
//@formatter:off
|
||||
ThemePropertyFileReader reader = new ThemePropertyFileReader("test", new StringReader(String.join("\n",
|
||||
"[Defaults]",
|
||||
" color.b.1 = white", // WHITE
|
||||
" color.b.2 = #ff0000", // RED
|
||||
"[Dark Defaults]",
|
||||
" color.b.1 = black", // BLACK
|
||||
" color.b.2 = #0000ff", // BLUE
|
||||
"")));
|
||||
//@formatter:on
|
||||
|
||||
GThemeValueMap values = reader.getDefaultValues();
|
||||
assertEquals(2, values.size());
|
||||
|
||||
GThemeValueMap darkValues = reader.getDarkDefaultValues();
|
||||
assertEquals(2, values.size());
|
||||
|
||||
assertEquals(WHITE, getColorOrRef(values, "color.b.1"));
|
||||
assertEquals(RED, getColorOrRef(values, "color.b.2"));
|
||||
assertEquals(BLACK, getColorOrRef(darkValues, "color.b.1"));
|
||||
assertEquals(BLUE, getColorOrRef(darkValues, "color.b.2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseColorError() throws IOException {
|
||||
//@formatter:off
|
||||
ThemePropertyFileReader reader = new ThemePropertyFileReader("test", new StringReader(String.join("\n",
|
||||
"[Defaults]",
|
||||
" color.b.1 = white", // WHITE
|
||||
" color.b.2 = sdfsdf", // RED
|
||||
"")));
|
||||
//@formatter:on
|
||||
List<String> errors = reader.getErrors();
|
||||
assertEquals(1, errors.size());
|
||||
assertEquals("Error parsing file \"test\" at line: 3, Could not parse Color: sdfsdf",
|
||||
errors.get(0));
|
||||
|
||||
}
|
||||
|
||||
private Object getColorOrRef(GThemeValueMap values, String id) {
|
||||
ColorValue color = values.getColor(id);
|
||||
if (color.getReferenceId() != null) {
|
||||
return color.getReferenceId();
|
||||
}
|
||||
return color.getRawValue();
|
||||
}
|
||||
|
||||
private Object getFontOrRef(GThemeValueMap values, String id) {
|
||||
FontValue font = values.getFont(id);
|
||||
if (font.getReferenceId() != null) {
|
||||
return font.getReferenceId();
|
||||
}
|
||||
return font.getRawValue();
|
||||
}
|
||||
|
||||
private Object getIconOrRef(GThemeValueMap values, String id) {
|
||||
IconValue icon = values.getIcon(id);
|
||||
if (icon.getReferenceId() != null) {
|
||||
return icon.getReferenceId();
|
||||
}
|
||||
return icon.getRawValue();
|
||||
}
|
||||
}
|
|
@ -135,6 +135,13 @@ public abstract class AbstractOptions implements Options {
|
|||
throw new IllegalArgumentException(
|
||||
"Can't register an option of type: " + OptionType.NO_TYPE);
|
||||
}
|
||||
|
||||
if (!type.isCompatible(defaultValue)) {
|
||||
throw new IllegalStateException(
|
||||
"Given default value does not match the given OptionType! OptionType = " + type +
|
||||
", defaultValue = " + defaultValue);
|
||||
}
|
||||
|
||||
if (type == OptionType.CUSTOM_TYPE && editor == null) {
|
||||
throw new IllegalStateException(
|
||||
"Can't register a custom option without a property editor");
|
||||
|
@ -173,8 +180,7 @@ public abstract class AbstractOptions implements Options {
|
|||
if (option == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!isCompatibleOption(option, type, defaultValue)) {
|
||||
if (option.getOptionType() != type) {
|
||||
Msg.error(this, "Registered option incompatible with existing option: " + optionName,
|
||||
new AssertException());
|
||||
return null;
|
||||
|
@ -182,15 +188,6 @@ public abstract class AbstractOptions implements Options {
|
|||
return option;
|
||||
}
|
||||
|
||||
private boolean isCompatibleOption(Option option, OptionType type, Object defaultValue) {
|
||||
if (option.getOptionType() != type) {
|
||||
return false;
|
||||
}
|
||||
Object optionValue = option.getValue(null);
|
||||
return optionValue == null || defaultValue == null ||
|
||||
optionValue.getClass().equals(defaultValue.getClass());
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void removeOption(String optionName) {
|
||||
aliasMap.remove(optionName);
|
||||
|
@ -700,7 +697,16 @@ public abstract class AbstractOptions implements Options {
|
|||
return true;
|
||||
}
|
||||
|
||||
return SUPPORTED_CLASSES.contains(obj.getClass());
|
||||
if (SUPPORTED_CLASSES.contains(obj.getClass())) {
|
||||
return true;
|
||||
}
|
||||
// check for extended classes
|
||||
for (Class<?> class1 : SUPPORTED_CLASSES) {
|
||||
if (class1.isAssignableFrom(obj.getClass())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -64,6 +64,10 @@ public enum OptionType {
|
|||
return stringAdapter.objectToString(object);
|
||||
}
|
||||
|
||||
public boolean isCompatible(Object object) {
|
||||
return object == null || clazz.isAssignableFrom(object.getClass());
|
||||
}
|
||||
|
||||
public Class<?> getValueClass() {
|
||||
return clazz;
|
||||
}
|
||||
|
@ -230,8 +234,8 @@ public enum OptionType {
|
|||
}
|
||||
catch (Exception e) {
|
||||
Msg.error(this,
|
||||
"Can't create customOption instance for: " + customOptionClassName +
|
||||
e);
|
||||
"Can't create customOption instance for: " + customOptionClassName +
|
||||
e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -18,8 +18,6 @@ package ghidra.util;
|
|||
import java.awt.Color;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Class for web color support. This class defines many of the colors used by html. This class
|
||||
|
@ -27,7 +25,7 @@ import java.util.regex.Pattern;
|
|||
* those strings back to a color.
|
||||
*/
|
||||
public abstract class WebColors {
|
||||
private static final Pattern HEX_PATTERN = Pattern.compile("(0x|#)[0-9A-Fa-f]{6}");
|
||||
// private static final Pattern HEX_PATTERN = Pattern.compile("(0x|#)[0-9A-Fa-f]{6}");
|
||||
private static final Map<String, Color> nameToColorMap = new HashMap<>();
|
||||
private static final Map<Color, String> colorToNameMap = new HashMap<>();
|
||||
|
||||
|
@ -172,12 +170,12 @@ public abstract class WebColors {
|
|||
public static final Color MEDUM_AQUA_MARINE = registerColor("MediumAquaMarine", Color.decode("0x66CDAA"));
|
||||
public static final Color MEDIUM_TURQOISE = registerColor("MediumTurquoise", Color.decode("0x48D1CC"));
|
||||
public static final Color DARK_OLIVE_GREEN = registerColor("DarkOliveGreen", Color.decode("0x556B2F"));
|
||||
public static final Color CORN_FLOWER_BLUE = registerColor("CornflowerBlue", Color.decode("0x6495ED"));
|
||||
public static final Color COMFLOWER_BLUE = registerColor("ComflowerBlue", Color.decode("0x6495ED"));
|
||||
//@formatter:on
|
||||
|
||||
// cannot instantiate nor extend
|
||||
private WebColors() {
|
||||
}
|
||||
private WebColors() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to find a color for the given String value. The String value can either be
|
||||
|
@ -193,57 +191,202 @@ public abstract class WebColors {
|
|||
return color != null ? color : defaultColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to find a color for the given String value. The String value can either be
|
||||
* a hex string (see {@link Color#decode(String)}) or a web color name as defined
|
||||
* in {@link WebColors}
|
||||
*
|
||||
* @param value the string value to interpret as a color
|
||||
* @return a color for the given string value or null if the string can't be translated
|
||||
*/
|
||||
public static Color getColor(String value) {
|
||||
Color color = nameToColorMap.get(value);
|
||||
if (color != null) {
|
||||
return color;
|
||||
}
|
||||
// if the value matches an RGB hex string, turn that into a color
|
||||
color = getHexColor(value);
|
||||
if (color != null) {
|
||||
return color;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a color to a string value. If there is a defined color for the given color value,
|
||||
* the color name will be returned. Otherwise, it will return a hex string for the color as
|
||||
* defined by {@link Color#toString()}. The result of this call can be passed to
|
||||
* {@link #getColor(String)} and be guaranteed that a color will be returned
|
||||
* follows. If the color has an non-opaque alpha value, it will be of the form #rrggbb. If
|
||||
* it has an alpha value,then the format will be #rrggbbaa.
|
||||
*
|
||||
* @param color the color to convert to a string.
|
||||
* @return the string representation for the given color.
|
||||
*/
|
||||
public static String toString(Color color) {
|
||||
String name = colorToNameMap.get(color);
|
||||
if (name != null) {
|
||||
return name;
|
||||
}
|
||||
// this will format a color value as a 6 digit hex (e.g. #rrggbb)
|
||||
return String.format("#%06X", color.getRGB() & 0xffffff);
|
||||
return toString(color, true);
|
||||
}
|
||||
|
||||
private static Color getHexColor(String hexString) {
|
||||
Matcher matcher = HEX_PATTERN.matcher(hexString);
|
||||
if (matcher.matches()) {
|
||||
return Color.decode(hexString);
|
||||
/**
|
||||
* Converts a color to a string value. If the color is a WebColor and the useNameIfPossible
|
||||
* is true, the name of the color will be returned. OOtherwise, it will return a hex string for the color as
|
||||
* follows. If the color has an non-opaque alpha value, it will be of the form #rrggbb. If
|
||||
* it has an alpha value ,then the format will be #rrggbbaa.
|
||||
*
|
||||
* @param color the color to convert to a string.
|
||||
* @param useNameIfPossible if true, the name of the color will be returned if the color is
|
||||
* a WebColor
|
||||
* @return the string representation for the given color.
|
||||
*/
|
||||
public static String toString(Color color, boolean useNameIfPossible) {
|
||||
if (useNameIfPossible) {
|
||||
String name = colorToNameMap.get(color);
|
||||
if (name != null) {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
int rgb = color.getRGB() & 0xffffff; //mask off any alpha value
|
||||
int alpha = color.getAlpha();
|
||||
if (alpha != 0xff) {
|
||||
return String.format("#%06x%02x", rgb, alpha);
|
||||
}
|
||||
return String.format("#%06x", rgb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the WebColor name for the given color. Returns null if the color is not a WebColor
|
||||
* @param color the color to lookup a WebColor name.
|
||||
* @return the WebColor name for the given color. Returns null if the color is not a WebColor
|
||||
*/
|
||||
public static String toWebColorName(Color color) {
|
||||
return colorToNameMap.get(color);
|
||||
}
|
||||
|
||||
private static Color registerColor(String name, Color color) {
|
||||
nameToColorMap.put(name, color);
|
||||
nameToColorMap.put(name.toLowerCase(), color);
|
||||
colorToNameMap.put(color, name);
|
||||
return color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to convert the given string into a color in a most flexible manner. It first checks
|
||||
* if the given string matches the name of a known web color as defined above. If so it
|
||||
* returns that color. Otherwise it tries to parse the string in any one of the following
|
||||
* formats:
|
||||
* <pre>
|
||||
* #rrggbb
|
||||
* #rrggbbaa
|
||||
* 0xrrggbb
|
||||
* 0xrrggbbaa
|
||||
* rgb(red, green, blue)
|
||||
* rgba(red, green, alpha)
|
||||
* </pre>
|
||||
* In the hex digit formats, the hex digits "rr", "gg", "bb", "aa" represent the values for red,
|
||||
* green, blue, and alpha, respectively. In the "rgb" and "rgba" formats the red, green, and
|
||||
* blue values are all integers between 0-255, while the alpha value is a float value from 0.0 to
|
||||
* 1.0.
|
||||
* <BR><BR>
|
||||
* @param colorString
|
||||
* @return a color for the given string or null
|
||||
*/
|
||||
public static Color getColor(String colorString) {
|
||||
String value = colorString.trim().toLowerCase();
|
||||
Color color = nameToColorMap.get(value.toLowerCase());
|
||||
if (color != null) {
|
||||
return color;
|
||||
}
|
||||
|
||||
return parseColor(value);
|
||||
}
|
||||
|
||||
private static Color parseColor(String colorString) {
|
||||
if (colorString.startsWith("#") || colorString.startsWith("0x")) {
|
||||
return parseHexColor(colorString);
|
||||
}
|
||||
if (colorString.startsWith("rgb(")) {
|
||||
return parseRGBColor(colorString);
|
||||
}
|
||||
return parseRGBAColor(colorString);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given string into a color. The string must be in one of the following formats:
|
||||
* <pre>
|
||||
* #rrggbb
|
||||
* #rrggbbaa
|
||||
* 0xrrggbb
|
||||
* 0xrrggbbaa
|
||||
* </pre>
|
||||
*
|
||||
* Each of the hex digits "rr", "gg", "bb", and "aa" specify the red, green, blue, and alpha
|
||||
* values respectively.
|
||||
* <br><br>
|
||||
*
|
||||
* @param hexString the string to parse into a color.
|
||||
* @return the parsed Color or null if the input string was invalid.
|
||||
*/
|
||||
private static Color parseHexColor(String hexString) {
|
||||
String value = hexString.trim();
|
||||
if (value.startsWith("#")) {
|
||||
value = value.substring(1);
|
||||
}
|
||||
else if (value.startsWith("0x")) {
|
||||
value = value.substring(2);
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (value.length() != 8 && value.length() != 6) {
|
||||
return null;
|
||||
}
|
||||
|
||||
boolean hasAlpha = value.length() == 8;
|
||||
if (hasAlpha) {
|
||||
// alpha value is the last 2 digits, Color wants alpha to be in upper bits so re-arrange
|
||||
value = value.substring(6) + value.substring(0, 6);
|
||||
}
|
||||
|
||||
try {
|
||||
long colorValue = Long.parseLong(value, 16);
|
||||
return new Color((int) colorValue, hasAlpha);
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given string into a color. The string must be in one of the following formats:
|
||||
* <pre>
|
||||
* rgb(red, green, blue)
|
||||
* rgb(red, green, blue, alpha)
|
||||
* </pre>
|
||||
* Each of the values "red", "green", "blue", and "alpha" must be integer values between 0-255
|
||||
* <br><br>
|
||||
* @param rgbString the string to parse into a color.
|
||||
* @return the parsed Color or null if the input string was invalid.
|
||||
*/
|
||||
private static Color parseRGBColor(String rgbString) {
|
||||
String value = rgbString.trim().replaceAll(" ", "");
|
||||
if (!(value.startsWith("rgb(") && value.endsWith(")"))) {
|
||||
return null;
|
||||
}
|
||||
// strip off to comma separated values
|
||||
value = value.substring(4, value.length() - 1);
|
||||
String[] split = value.split(",");
|
||||
if (split.length != 3) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
int red = Integer.parseInt(split[0]);
|
||||
int green = Integer.parseInt(split[1]);
|
||||
int blue = Integer.parseInt(split[2]);
|
||||
return new Color(red, green, blue);
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static Color parseRGBAColor(String rgbaString) {
|
||||
String value = rgbaString.replaceAll(" ", "");
|
||||
if (!(value.startsWith("rgba(") && value.endsWith(")"))) {
|
||||
return null;
|
||||
}
|
||||
// strip off to comma separated values
|
||||
value = value.substring(5, value.length() - 1);
|
||||
value = value.replaceAll(" ", "");
|
||||
String[] split = value.split(",");
|
||||
if (split.length != 4) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
int red = Integer.parseInt(split[0]);
|
||||
int green = Integer.parseInt(split[1]);
|
||||
int blue = Integer.parseInt(split[2]);
|
||||
float alpha = Float.parseFloat(split[3]);
|
||||
return new Color(red, green, blue, (int) (alpha * 255 + 0.5));
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,27 +39,16 @@ public class WebColorsTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testGetColorFromName() {
|
||||
public void testGetColor() {
|
||||
assertEquals(WebColors.NAVY, WebColors.getColor("Navy"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetColorFromHexString() {
|
||||
assertEquals(WebColors.NAVY, WebColors.getColor("0x000080"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetColorFromHexString2() {
|
||||
assertEquals(WebColors.NAVY, WebColors.getColor("#000080"));
|
||||
}
|
||||
assertEquals(WebColors.NAVY, WebColors.getColor("rgb(0,0,128)"));
|
||||
assertEquals(WebColors.NAVY, WebColors.getColor("rgba(0,0,128,1.0)"));
|
||||
|
||||
@Test
|
||||
public void testGetColorWithNoDefinedValue() {
|
||||
assertEquals(new Color(0x12, 0x34, 0x56), WebColors.getColor("0x123456"));
|
||||
}
|
||||
assertEquals(new Color(0x123456), WebColors.getColor("0x123456"));
|
||||
assertEquals(new Color(0x80102030, true), WebColors.getColor("rgba(16, 32, 48, 0.5)"));
|
||||
|
||||
@Test
|
||||
public void testGetColorByBadName() {
|
||||
assertNull(WebColors.getColor("ABCDEFG"));
|
||||
assertNull(WebColors.getColor("asdfasdfas"));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
##MODULE IP: Oxygen Icons - LGPL 3.0
|
||||
Module.manifest||GHIDRA||||END|
|
||||
data/ExtensionPoint.manifest||GHIDRA||||END|
|
||||
data/graph.theme.properties||GHIDRA||||END|
|
||||
src/main/docs/README.txt||GHIDRA||||END|
|
||||
src/main/docs/VerticesAndEdges.png||GHIDRA||||END|
|
||||
src/main/docs/VerticesAndEdges.xml||GHIDRA||||END|
|
||||
|
|
13
Ghidra/Framework/Graph/data/graph.theme.properties
Normal file
13
Ghidra/Framework/Graph/data/graph.theme.properties
Normal file
|
@ -0,0 +1,13 @@
|
|||
[Defaults]
|
||||
|
||||
|
||||
color.bg.visualgraph = color.bg
|
||||
color.bg.highlight.visualgraph = rgba(255,255,0,0.6) // somewhat transparent yellow
|
||||
|
||||
color.graph.display.vertex = green
|
||||
color.graph.display.edge = green
|
||||
color.graph.display.vertex.selected = blue
|
||||
color.graph.display.edge.selected = blue
|
||||
|
||||
[Dark Defaults]
|
||||
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue