GP-1981 Theme help and module conversions

This commit is contained in:
ghidragon 2022-09-13 16:36:26 -04:00
parent 4eb3d8fd86
commit 9a0d7892da
120 changed files with 1540 additions and 2846 deletions

View file

@ -67,6 +67,7 @@
<tocdef id="Ghidra Project Window" sortgroup="g" text="Ghidra Project Window" target="help/topics/FrontEndPlugin/Ghidra_Front_end.htm#Project_Window" /> <tocdef id="Ghidra Project Window" sortgroup="g" text="Ghidra Project Window" target="help/topics/FrontEndPlugin/Ghidra_Front_end.htm#Project_Window" />
<tocdef id="Ghidra Server" sortgroup="h" text="Ghidra Server" target="help/topics/GhidraServer/GhidraServer.htm" /> <tocdef id="Ghidra Server" sortgroup="h" text="Ghidra Server" target="help/topics/GhidraServer/GhidraServer.htm" />
<tocdef id="About Ghidra" sortgroup="i" text="About Ghidra" target="help/topics/About/About_Ghidra.htm" /> <tocdef id="About Ghidra" sortgroup="i" text="About Ghidra" target="help/topics/About/About_Ghidra.htm" />
</tocdef> </tocdef>

View file

@ -15,7 +15,6 @@
*/ */
package ghidra.app.plugin.core.function.editor; package ghidra.app.plugin.core.function.editor;
import java.awt.Color;
import java.awt.Component; import java.awt.Component;
import java.awt.event.*; import java.awt.event.*;
import java.math.BigInteger; import java.math.BigInteger;
@ -28,6 +27,7 @@ import javax.swing.table.TableCellEditor;
import docking.widgets.combobox.GhidraComboBox; import docking.widgets.combobox.GhidraComboBox;
import docking.widgets.textfield.IntegerTextField; import docking.widgets.textfield.IntegerTextField;
import generic.theme.GThemeDefaults.Colors.Palette;
import ghidra.app.util.AddressInput; import ghidra.app.util.AddressInput;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressOutOfBoundsException; import ghidra.program.model.address.AddressOutOfBoundsException;
@ -44,12 +44,7 @@ class VarnodeLocationCellEditor extends AbstractCellEditor implements TableCellE
private AddressInput addressInput; private AddressInput addressInput;
private IntegerTextField offsetInput; private IntegerTextField offsetInput;
private Comparator<Register> registerWrapperComparator = new Comparator<Register>() { private Comparator<Register> registerWrapperComparator = (r1, r2) -> r1.toString().compareToIgnoreCase(r2.toString());
@Override
public int compare(Register r1, Register r2) {
return r1.toString().compareToIgnoreCase(r2.toString());
}
};
private VarnodeInfo currentVarnode; private VarnodeInfo currentVarnode;
private int maxRegisterSize; private int maxRegisterSize;
@ -146,12 +141,7 @@ class VarnodeLocationCellEditor extends AbstractCellEditor implements TableCellE
if (address != null) { if (address != null) {
addressInput.setAddress(address); addressInput.setAddress(address);
} }
addressInput.addActionListener(new ActionListener() { addressInput.addActionListener(e -> stopCellEditing());
@Override
public void actionPerformed(ActionEvent e) {
stopCellEditing();
}
});
return addressInput; return addressInput;
} }
@ -162,14 +152,9 @@ class VarnodeLocationCellEditor extends AbstractCellEditor implements TableCellE
if (address != null) { if (address != null) {
offsetInput.setValue(address.getOffset()); offsetInput.setValue(address.getOffset());
} }
offsetInput.addActionListener(new ActionListener() { offsetInput.addActionListener(e -> stopCellEditing());
@Override
public void actionPerformed(ActionEvent e) {
stopCellEditing();
}
});
JComponent component = offsetInput.getComponent(); JComponent component = offsetInput.getComponent();
component.setBorder(BorderFactory.createLineBorder(Color.GRAY, 1)); component.setBorder(BorderFactory.createLineBorder(Palette.GRAY, 1));
return component; return component;
} }
@ -215,12 +200,7 @@ class VarnodeLocationCellEditor extends AbstractCellEditor implements TableCellE
} }
}); });
combo.addActionListener(new ActionListener() { combo.addActionListener(e -> stopCellEditing());
@Override
public void actionPerformed(ActionEvent e) {
stopCellEditing();
}
});
return combo; return combo;
} }

View file

@ -15,7 +15,6 @@
*/ */
package ghidra.app.plugin.core.function.editor; package ghidra.app.plugin.core.function.editor;
import java.awt.Color;
import java.awt.Component; import java.awt.Component;
import java.awt.event.*; import java.awt.event.*;
import java.math.BigInteger; import java.math.BigInteger;
@ -25,6 +24,7 @@ import javax.swing.*;
import javax.swing.table.TableCellEditor; import javax.swing.table.TableCellEditor;
import docking.widgets.textfield.IntegerTextField; import docking.widgets.textfield.IntegerTextField;
import generic.theme.GThemeDefaults.Colors.Palette;
class VarnodeSizeCellEditor extends AbstractCellEditor implements TableCellEditor { class VarnodeSizeCellEditor extends AbstractCellEditor implements TableCellEditor {
@ -70,14 +70,9 @@ class VarnodeSizeCellEditor extends AbstractCellEditor implements TableCellEdito
}; };
input.getComponent().addFocusListener(focusListener); input.getComponent().addFocusListener(focusListener);
} }
input.addActionListener(new ActionListener() { input.addActionListener(e -> stopCellEditing());
@Override
public void actionPerformed(ActionEvent e) {
stopCellEditing();
}
});
JComponent component = input.getComponent(); JComponent component = input.getComponent();
component.setBorder(BorderFactory.createLineBorder(Color.GRAY, 1)); component.setBorder(BorderFactory.createLineBorder(Palette.GRAY, 1));
return component; return component;
} }

View file

@ -27,6 +27,7 @@ import javax.swing.*;
import docking.widgets.OptionDialog; import docking.widgets.OptionDialog;
import docking.widgets.table.*; import docking.widgets.table.*;
import generic.theme.GColor; import generic.theme.GColor;
import generic.theme.GThemeDefaults.Colors.Palette;
import ghidra.app.cmd.register.SetRegisterCmd; import ghidra.app.cmd.register.SetRegisterCmd;
import ghidra.app.events.ProgramSelectionPluginEvent; import ghidra.app.events.ProgramSelectionPluginEvent;
import ghidra.app.services.*; import ghidra.app.services.*;
@ -492,7 +493,7 @@ class RegisterValueRange {
class RegisterValueRenderer extends GTableCellRenderer { class RegisterValueRenderer extends GTableCellRenderer {
private Color defaultColor = Color.LIGHT_GRAY; private Color defaultColor = Palette.LIGHT_GRAY;
RegisterValueRenderer(JTable table) { RegisterValueRenderer(JTable table) {
setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 0)); setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 0));

View file

@ -15,10 +15,9 @@
*/ */
package ghidra.app.util.viewer.field; package ghidra.app.util.viewer.field;
import java.awt.Color;
import docking.widgets.fieldpanel.field.AttributedString; import docking.widgets.fieldpanel.field.AttributedString;
import generic.theme.GThemeDefaults.Colors.Messages; import generic.theme.GThemeDefaults.Colors.Messages;
import generic.theme.GThemeDefaults.Colors.Palette;
import ghidra.app.nav.Navigatable; import ghidra.app.nav.Navigatable;
import ghidra.app.services.GoToService; import ghidra.app.services.GoToService;
import ghidra.framework.plugintool.ServiceProvider; import ghidra.framework.plugintool.ServiceProvider;
@ -76,7 +75,7 @@ public class AddressAnnotatedStringHandler implements AnnotatedStringHandler {
buffer.append(string).append(" "); buffer.append(string).append(" ");
} }
return new AttributedString(buffer.toString(), Color.LIGHT_GRAY, return new AttributedString(buffer.toString(), Palette.LIGHT_GRAY,
prototypeString.getFontMetrics(0)); prototypeString.getFontMetrics(0));
} }

View file

@ -22,6 +22,7 @@ import javax.swing.JComponent;
import docking.widgets.fieldpanel.internal.FieldBackgroundColorManager; import docking.widgets.fieldpanel.internal.FieldBackgroundColorManager;
import docking.widgets.fieldpanel.internal.PaintContext; import docking.widgets.fieldpanel.internal.PaintContext;
import docking.widgets.fieldpanel.support.*; import docking.widgets.fieldpanel.support.*;
import generic.theme.GThemeDefaults.Colors.Palette;
import ghidra.app.util.viewer.format.FieldFormatModel; import ghidra.app.util.viewer.format.FieldFormatModel;
import ghidra.app.util.viewer.proxy.EmptyProxy; import ghidra.app.util.viewer.proxy.EmptyProxy;
import ghidra.app.util.viewer.proxy.ProxyObj; import ghidra.app.util.viewer.proxy.ProxyObj;
@ -177,7 +178,7 @@ public class IndentField implements ListingField {
public void paint(JComponent c, Graphics g, PaintContext context, public void paint(JComponent c, Graphics g, PaintContext context,
Rectangle clip, FieldBackgroundColorManager map, RowColLocation cursorLoc, Rectangle clip, FieldBackgroundColorManager map, RowColLocation cursorLoc,
int rowHeight) { int rowHeight) {
g.setColor(Color.LIGHT_GRAY); g.setColor(Palette.LIGHT_GRAY);
// draw the vertical lines to the left of the data (these are shown when there are vertical // draw the vertical lines to the left of the data (these are shown when there are vertical
// bars drawn for inset data) // bars drawn for inset data)

View file

@ -23,6 +23,7 @@ import javax.swing.JComponent;
import docking.widgets.fieldpanel.internal.FieldBackgroundColorManager; import docking.widgets.fieldpanel.internal.FieldBackgroundColorManager;
import docking.widgets.fieldpanel.internal.PaintContext; import docking.widgets.fieldpanel.internal.PaintContext;
import docking.widgets.fieldpanel.support.*; import docking.widgets.fieldpanel.support.*;
import generic.theme.GThemeDefaults.Colors.Palette;
import ghidra.app.util.viewer.format.FieldFormatModel; import ghidra.app.util.viewer.format.FieldFormatModel;
import ghidra.app.util.viewer.proxy.EmptyProxy; import ghidra.app.util.viewer.proxy.EmptyProxy;
import ghidra.app.util.viewer.proxy.ProxyObj; import ghidra.app.util.viewer.proxy.ProxyObj;
@ -176,7 +177,7 @@ public class OpenCloseField implements ListingField {
} }
} }
g.setColor(Color.LIGHT_GRAY); g.setColor(Palette.LIGHT_GRAY);
// draw the vertical lines to the left of the toggle handle (these are shown when // draw the vertical lines to the left of the toggle handle (these are shown when
// there are vertical bars drawn for inset data) // there are vertical bars drawn for inset data)

View file

@ -35,6 +35,7 @@ import docking.widgets.fieldpanel.internal.FieldPanelCoordinator;
import docking.widgets.fieldpanel.listener.FieldLocationListener; import docking.widgets.fieldpanel.listener.FieldLocationListener;
import docking.widgets.fieldpanel.support.FieldLocation; import docking.widgets.fieldpanel.support.FieldLocation;
import docking.widgets.fieldpanel.support.ViewerPosition; import docking.widgets.fieldpanel.support.ViewerPosition;
import generic.theme.GThemeDefaults.Colors.Palette;
import ghidra.GhidraOptions; import ghidra.GhidraOptions;
import ghidra.app.nav.Navigatable; import ghidra.app.nav.Navigatable;
import ghidra.app.plugin.core.codebrowser.MarkerServiceBackgroundColorModel; import ghidra.app.plugin.core.codebrowser.MarkerServiceBackgroundColorModel;
@ -72,6 +73,8 @@ public class ListingCodeComparisonPanel
extends CodeComparisonPanel<ListingComparisonFieldPanelCoordinator> implements extends CodeComparisonPanel<ListingComparisonFieldPanelCoordinator> implements
FormatModelListener, CodeFormatService, ListingDiffChangeListener, OptionsChangeListener { FormatModelListener, CodeFormatService, ListingDiffChangeListener, OptionsChangeListener {
private static final Color FG_COLOR_TITLE = Palette.DARK_GRAY;
private static final String DUAL_LISTING_HEADER_SHOWING = "DUAL_LISTING_HEADER_SHOWING"; private static final String DUAL_LISTING_HEADER_SHOWING = "DUAL_LISTING_HEADER_SHOWING";
private static final String DUAL_LISTING_SIDE_BY_SIDE = "DUAL_LISTING_SIDE_BY_SIDE"; private static final String DUAL_LISTING_SIDE_BY_SIDE = "DUAL_LISTING_SIDE_BY_SIDE";
public static final String NAME = "DualListing"; public static final String NAME = "DualListing";
@ -1406,7 +1409,7 @@ public class ListingCodeComparisonPanel
String programStr = String programStr =
HTMLUtilities.friendlyEncodeHTML(program.getDomainFile().getPathname()); HTMLUtilities.friendlyEncodeHTML(program.getDomainFile().getPathname());
String specialProgramStr = HTMLUtilities.colorString(Color.DARK_GRAY, programStr); String specialProgramStr = HTMLUtilities.colorString(FG_COLOR_TITLE, programStr);
buf.append(specialProgramStr); buf.append(specialProgramStr);
buf.append(padStr); buf.append(padStr);
} }
@ -1441,7 +1444,7 @@ public class ListingCodeComparisonPanel
String programStr = String programStr =
HTMLUtilities.friendlyEncodeHTML(program.getDomainFile().getPathname()); HTMLUtilities.friendlyEncodeHTML(program.getDomainFile().getPathname());
String specialProgramStr = HTMLUtilities.colorString(Color.DARK_GRAY, programStr); String specialProgramStr = HTMLUtilities.colorString(FG_COLOR_TITLE, programStr);
buf.append(specialProgramStr); buf.append(specialProgramStr);
buf.append(padStr); buf.append(padStr);
} }
@ -1461,7 +1464,7 @@ public class ListingCodeComparisonPanel
String padStr = HTMLUtilities.spaces(4); String padStr = HTMLUtilities.spaces(4);
buf.append(padStr); buf.append(padStr);
String programStr = HTMLUtilities.friendlyEncodeHTML(program.getDomainFile().getPathname()); String programStr = HTMLUtilities.friendlyEncodeHTML(program.getDomainFile().getPathname());
String specialProgramStr = HTMLUtilities.colorString(Color.DARK_GRAY, programStr); String specialProgramStr = HTMLUtilities.colorString(FG_COLOR_TITLE, programStr);
buf.append(specialProgramStr); buf.append(specialProgramStr);
buf.append(padStr); buf.append(padStr);
return HTMLUtilities.wrapAsHTML(buf.toString()); return HTMLUtilities.wrapAsHTML(buf.toString());

View file

@ -15,11 +15,11 @@
*/ */
package ghidra.app.util.xml; package ghidra.app.util.xml;
import java.awt.Color;
import java.util.*; import java.util.*;
import javax.swing.ImageIcon; import javax.swing.ImageIcon;
import generic.theme.GThemeDefaults.Colors.Palette;
import ghidra.app.cmd.function.*; import ghidra.app.cmd.function.*;
import ghidra.app.services.DataTypeManagerService; import ghidra.app.services.DataTypeManagerService;
import ghidra.app.util.NamespaceUtils; import ghidra.app.util.NamespaceUtils;
@ -129,7 +129,8 @@ class FunctionsXmlMgr {
if (bt == null) { if (bt == null) {
ImageIcon icon = ImageIcon icon =
ResourceManager.loadImage("images/imported_bookmark.gif"); ResourceManager.loadImage("images/imported_bookmark.gif");
bt = bm.defineType("IMPORTED", icon, Color.DARK_GRAY, 0); bt = bm.defineType("IMPORTED", icon, Palette.DARK_GRAY,
0);
} }
bm.setBookmark(entryPoint, "IMPORTED", LIB_BOOKMARK_CATEGORY, bm.setBookmark(entryPoint, "IMPORTED", LIB_BOOKMARK_CATEGORY,
"Library function"); "Library function");
@ -680,7 +681,7 @@ class FunctionsXmlMgr {
private Parameter[] getRegisterParameters(Function function) { private Parameter[] getRegisterParameters(Function function) {
ArrayList<Parameter> list = new ArrayList<Parameter>(); ArrayList<Parameter> list = new ArrayList<>();
Parameter[] params = function.getParameters(); Parameter[] params = function.getParameters();
for (Parameter param : params) { for (Parameter param : params) {
if (param.isRegisterVariable()) { if (param.isRegisterVariable()) {

View file

@ -15,9 +15,9 @@
*/ */
package ghidra.program.util; package ghidra.program.util;
import java.awt.Color;
import java.util.*; import java.util.*;
import generic.theme.GThemeDefaults.Colors.Palette;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.data.DataType; import ghidra.program.model.data.DataType;
import ghidra.program.model.data.GenericCallingConvention; import ghidra.program.model.data.GenericCallingConvention;
@ -381,7 +381,7 @@ public class FunctionUtility {
String programStr = String programStr =
HTMLUtilities.friendlyEncodeHTML(program.getDomainFile().getPathname()); HTMLUtilities.friendlyEncodeHTML(program.getDomainFile().getPathname());
String specialProgramStr = HTMLUtilities.colorString(Color.DARK_GRAY, programStr); String specialProgramStr = HTMLUtilities.colorString(Palette.DARK_GRAY, programStr);
buf.append(specialProgramStr); buf.append(specialProgramStr);
buf.append(padStr); buf.append(padStr);
} }

View file

@ -23,6 +23,7 @@ import javax.swing.ImageIcon;
import javax.swing.JLabel; import javax.swing.JLabel;
import docking.widgets.table.GTableCellRenderingData; import docking.widgets.table.GTableCellRenderingData;
import generic.theme.GThemeDefaults.Colors;
import ghidra.docking.settings.Settings; import ghidra.docking.settings.Settings;
import ghidra.framework.plugintool.ServiceProvider; import ghidra.framework.plugintool.ServiceProvider;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
@ -75,7 +76,7 @@ public class MemoryTypeProgramLocationBasedTableColumn
private class MemoryTypeRenderer extends AbstractGhidraColumnRenderer<MemoryBlock> { private class MemoryTypeRenderer extends AbstractGhidraColumnRenderer<MemoryBlock> {
private Color disabledColor = Color.LIGHT_GRAY; private Color disabledColor = Colors.DISABLED;
private ImageIcon offIcon = ResourceManager.loadImage("images/EmptyIcon16.gif"); private ImageIcon offIcon = ResourceManager.loadImage("images/EmptyIcon16.gif");
private ImageIcon onIcon = ResourceManager.loadImage("images/check.png"); private ImageIcon onIcon = ResourceManager.loadImage("images/check.png");

View file

@ -38,6 +38,14 @@ import ghidra.framework.main.FrontEndTool;
import ghidra.util.Msg; import ghidra.util.Msg;
import ghidra.util.exception.AssertException; import ghidra.util.exception.AssertException;
/**
* Extend this class to create screen shot images for help. The name of the class determines the
* topic directory where the captured image will be stored. So if the class name is
* XyzShreenShots, the resulting captured image will appear in help topic directy "Xyz", regardless
* of which module has that topic. The test name will determine the name of the image file
* that is generated. So if the test name is testHappyBirthday, the filename will be
* HappyBirthday.png.
*/
public abstract class GhidraScreenShotGenerator extends AbstractScreenShotGenerator { public abstract class GhidraScreenShotGenerator extends AbstractScreenShotGenerator {
private static final Color FG_COLOR_TEXT = Palette.getColor("color.palate.cornflowerblue"); private static final Color FG_COLOR_TEXT = Palette.getColor("color.palate.cornflowerblue");

View file

@ -25,6 +25,8 @@ import docking.*;
import docking.action.DockingAction; import docking.action.DockingAction;
import docking.action.MenuData; import docking.action.MenuData;
import docking.widgets.table.GFilterTable; import docking.widgets.table.GFilterTable;
import generic.theme.GColor;
import generic.theme.GThemeDefaults.Colors;
import ghidra.bitpatterns.info.*; import ghidra.bitpatterns.info.*;
import ghidra.util.HelpLocation; import ghidra.util.HelpLocation;
import ghidra.util.bytesearch.DittedBitSequence; import ghidra.util.bytesearch.DittedBitSequence;
@ -35,6 +37,8 @@ import resources.ResourceManager;
*/ */
public abstract class ByteSequenceAnalyzerProvider extends DialogComponentProvider { public abstract class ByteSequenceAnalyzerProvider extends DialogComponentProvider {
private static final Color BG_DISABLED = new GColor("color.bg.uneditable");
protected ByteSequenceTableModel byteSequenceTable; protected ByteSequenceTableModel byteSequenceTable;
protected FunctionBitPatternsExplorerPlugin plugin; protected FunctionBitPatternsExplorerPlugin plugin;
protected JPanel mainPanel; protected JPanel mainPanel;
@ -175,9 +179,9 @@ public abstract class ByteSequenceAnalyzerProvider extends DialogComponentProvid
} }
mergedSeqTextField.setText(merged.getHexString()); mergedSeqTextField.setText(merged.getHexString());
bitsOfCheckField.setText(Integer.toString(merged.getNumFixedBits())); bitsOfCheckField.setText(Integer.toString(merged.getNumFixedBits()));
mergedSeqTextField.setBackground(Color.WHITE); mergedSeqTextField.setBackground(Colors.BACKGROUND);
bitsOfCheckField.setBackground(Color.WHITE); bitsOfCheckField.setBackground(Colors.BACKGROUND);
noteField.setBackground(Color.WHITE); noteField.setBackground(Colors.BACKGROUND);
mergedToSend = true; mergedToSend = true;
} }
@ -211,9 +215,9 @@ public abstract class ByteSequenceAnalyzerProvider extends DialogComponentProvid
mergedInfo.setNote(note); mergedInfo.setNote(note);
plugin.addPattern(mergedInfo); plugin.addPattern(mergedInfo);
plugin.updateClipboard(); plugin.updateClipboard();
mergedSeqTextField.setBackground(Color.lightGray); mergedSeqTextField.setBackground(BG_DISABLED);
bitsOfCheckField.setBackground(Color.LIGHT_GRAY); bitsOfCheckField.setBackground(BG_DISABLED);
noteField.setBackground(Color.LIGHT_GRAY); noteField.setBackground(BG_DISABLED);
mergedToSend = false; mergedToSend = false;
} }
} }

View file

@ -1,6 +1,7 @@
[Defaults] [Defaults]
color.bg.byteviewer = color.bg color.bg.byteviewer = color.bg
color.bg.byteviewer.highlight = yellow
color.fg.byteviewer.novalue = blue color.fg.byteviewer.novalue = blue
color.fg.byteviewer.changed = red color.fg.byteviewer.changed = red
@ -8,7 +9,12 @@ color.cursor.focused.byteviewer.current = color.cursor.focused
color.cursor.focused.byteviewer.noncurrent = black color.cursor.focused.byteviewer.noncurrent = black
color.cursor.unfocused.byteviewer = color.cursor.unfocused color.cursor.unfocused.byteviewer = color.cursor.unfocused
[Dark Defaults] [Dark Defaults]
color.bg.byteviewer.highlight = rgb(191, 191, 64) // olive
color.fg.byteviewer.novalue = DarkBlue color.fg.byteviewer.novalue = DarkBlue
color.fg.byteviewer.changed = indianRed color.fg.byteviewer.changed = indianRed
color.cursor.focused.byteviewer.current = color.cursor.focused color.cursor.focused.byteviewer.current = color.cursor.focused

View file

@ -29,6 +29,7 @@ import docking.widgets.fieldpanel.Layout;
import docking.widgets.fieldpanel.field.Field; import docking.widgets.fieldpanel.field.Field;
import docking.widgets.fieldpanel.listener.*; import docking.widgets.fieldpanel.listener.*;
import docking.widgets.fieldpanel.support.*; import docking.widgets.fieldpanel.support.*;
import generic.theme.GColor;
import ghidra.app.plugin.core.format.*; import ghidra.app.plugin.core.format.*;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.util.Msg; import ghidra.util.Msg;
@ -885,7 +886,7 @@ public class ByteViewerComponent extends FieldPanel implements FieldMouseListene
private class ByteViewerBackgroundColorModel implements BackgroundColorModel { private class ByteViewerBackgroundColorModel implements BackgroundColorModel {
private Color defaultBackgroundColor = Color.WHITE; private Color defaultBackgroundColor = new GColor("color.bg.byteviewer");
@Override @Override
public Color getBackgroundColor(BigInteger index) { public Color getBackgroundColor(BigInteger index) {

View file

@ -1,6 +1,5 @@
/* ### /* ###
* IP: GHIDRA * IP: GHIDRA
* REVIEWED: YES
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,18 +15,19 @@
*/ */
package ghidra.app.plugin.core.byteviewer; package ghidra.app.plugin.core.byteviewer;
import ghidra.app.util.HighlightProvider;
import ghidra.app.util.viewer.field.FieldFactory;
import java.awt.Color; import java.awt.Color;
import docking.widgets.fieldpanel.support.Highlight; import docking.widgets.fieldpanel.support.Highlight;
import generic.theme.GColor;
import ghidra.app.util.HighlightProvider;
import ghidra.app.util.viewer.field.FieldFactory;
public class ByteViewerHighlightProvider implements HighlightProvider { public class ByteViewerHighlightProvider implements HighlightProvider {
private static Highlight[] NO_HIGHLIGHTS = new Highlight[0]; private static Highlight[] NO_HIGHLIGHTS = new Highlight[0];
private String highlightText; private String highlightText;
private Color highlightColor = Color.YELLOW; private Color highlightColor = new GColor("color.bg.byteviewer.highlight");
@Override
public Highlight[] getHighlights(String text, Object obj, public Highlight[] getHighlights(String text, Object obj,
Class<? extends FieldFactory> fieldFactoryClass, int cursorTextOffset) { Class<? extends FieldFactory> fieldFactoryClass, int cursorTextOffset) {

View file

@ -15,12 +15,12 @@
*/ */
package ghidra.app.plugin.core.byteviewer; package ghidra.app.plugin.core.byteviewer;
import java.awt.*; import java.awt.Component;
import java.awt.GridLayout;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.math.BigInteger; import java.math.BigInteger;
import java.util.*; import java.util.*;
import java.util.List;
import java.util.Map.Entry; import java.util.Map.Entry;
import javax.swing.*; import javax.swing.*;
@ -31,6 +31,8 @@ import javax.swing.event.ChangeListener;
import docking.DialogComponentProvider; import docking.DialogComponentProvider;
import docking.widgets.checkbox.GCheckBox; import docking.widgets.checkbox.GCheckBox;
import docking.widgets.label.GLabel; import docking.widgets.label.GLabel;
import generic.theme.GThemeDefaults.Colors;
import generic.theme.GThemeDefaults.Colors.Messages;
import ghidra.app.plugin.core.format.ByteBlockSelection; import ghidra.app.plugin.core.format.ByteBlockSelection;
import ghidra.app.plugin.core.format.DataFormatModel; import ghidra.app.plugin.core.format.DataFormatModel;
import ghidra.app.util.AddressInput; import ghidra.app.util.AddressInput;
@ -266,10 +268,10 @@ public class ByteViewerOptionsDialog extends DialogComponentProvider
JCheckBox checkBox = entry.getValue(); JCheckBox checkBox = entry.getValue();
DataFormatModel model = provider.getDataFormatModel(entry.getKey()); DataFormatModel model = provider.getDataFormatModel(entry.getKey());
if (model.validateBytesPerLine(bytesPerLine)) { if (model.validateBytesPerLine(bytesPerLine)) {
checkBox.setForeground(Color.BLACK); checkBox.setForeground(Colors.FOREGROUND);
} }
else { else {
checkBox.setForeground(Color.RED); checkBox.setForeground(Messages.ERROR);
isBad |= checkBox.isSelected(); isBad |= checkBox.isSelected();
} }
} }

View file

@ -23,7 +23,6 @@ import java.util.List;
import javax.swing.*; import javax.swing.*;
import javax.swing.event.*; import javax.swing.event.*;
import docking.help.HelpService;
import docking.widgets.fieldpanel.*; import docking.widgets.fieldpanel.*;
import docking.widgets.fieldpanel.field.EmptyTextField; import docking.widgets.fieldpanel.field.EmptyTextField;
import docking.widgets.fieldpanel.field.Field; import docking.widgets.fieldpanel.field.Field;
@ -43,6 +42,7 @@ import ghidra.util.exception.InvalidInputException;
import ghidra.util.layout.HorizontalLayout; import ghidra.util.layout.HorizontalLayout;
import ghidra.util.layout.PairLayout; import ghidra.util.layout.PairLayout;
import help.Help; import help.Help;
import help.HelpService;
/** /**
* Top level component that contains has a scrolled pane for the panel of components that show the * Top level component that contains has a scrolled pane for the panel of components that show the

View file

@ -34,6 +34,7 @@ import docking.action.DockingActionIf;
import docking.action.ToggleDockingAction; import docking.action.ToggleDockingAction;
import docking.widgets.EventTrigger; import docking.widgets.EventTrigger;
import docking.widgets.fieldpanel.support.*; import docking.widgets.fieldpanel.support.*;
import generic.theme.GThemeDefaults.Colors.Palette;
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin; import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
import ghidra.app.plugin.core.format.*; import ghidra.app.plugin.core.format.*;
import ghidra.app.plugin.core.navigation.*; import ghidra.app.plugin.core.navigation.*;
@ -1215,10 +1216,10 @@ public class ByteViewerPlugin2Test extends AbstractGhidraHeadedIntegrationTest {
Options opt = tool.getOptions("ByteViewer"); Options opt = tool.getOptions("ByteViewer");
// change the color for Current View Cursor Color // change the color for Current View Cursor Color
putColor(opt, ByteViewerComponentProvider.OPTION_CURRENT_VIEW_CURSOR_COLOR, Color.GREEN); putColor(opt, ByteViewerComponentProvider.OPTION_CURRENT_VIEW_CURSOR_COLOR, Palette.GREEN);
ByteViewerComponent c = panel.getCurrentComponent(); ByteViewerComponent c = panel.getCurrentComponent();
assertEquals(Color.GREEN, c.getFocusedCursorColor()); assertEquals(Palette.GREEN, c.getFocusedCursorColor());
} }
@Test @Test
@ -1235,10 +1236,10 @@ public class ByteViewerPlugin2Test extends AbstractGhidraHeadedIntegrationTest {
Options opt = tool.getOptions("ByteViewer"); Options opt = tool.getOptions("ByteViewer");
// change the color for Current View Cursor Color // change the color for Current View Cursor Color
putColor(opt, ByteViewerComponentProvider.OPTION_CURSOR_COLOR, Color.GREEN); putColor(opt, ByteViewerComponentProvider.OPTION_CURSOR_COLOR, Palette.GREEN);
ByteViewerComponent c = findComponent(panel, "Octal"); ByteViewerComponent c = findComponent(panel, "Octal");
assertEquals(Color.GREEN, c.getNonFocusCursorColor()); assertEquals(Palette.GREEN, c.getNonFocusCursorColor());
} }
@Test @Test
@ -1254,10 +1255,10 @@ public class ByteViewerPlugin2Test extends AbstractGhidraHeadedIntegrationTest {
Options opt = tool.getOptions("ByteViewer"); Options opt = tool.getOptions("ByteViewer");
// change the color for Current View Cursor Color // change the color for Current View Cursor Color
putColor(opt, ByteViewerComponentProvider.OPTION_NONFOCUS_CURSOR_COLOR, Color.CYAN); putColor(opt, ByteViewerComponentProvider.OPTION_NONFOCUS_CURSOR_COLOR, Palette.CYAN);
ByteViewerComponent c = findComponent(panel, "Octal"); ByteViewerComponent c = findComponent(panel, "Octal");
assertEquals(Color.CYAN, c.getNonFocusCursorColor()); assertEquals(Palette.CYAN, c.getNonFocusCursorColor());
} }
private void putFont(final Options options, final String optionName, final Font font) { private void putFont(final Options options, final String optionName, final Font font) {
@ -1282,7 +1283,7 @@ public class ByteViewerPlugin2Test extends AbstractGhidraHeadedIntegrationTest {
Options opt = tool.getOptions("ByteViewer"); Options opt = tool.getOptions("ByteViewer");
// change the color for Edit Color // change the color for Edit Color
putColor(opt, ByteViewerComponentProvider.OPTION_EDIT_COLOR, Color.GREEN); putColor(opt, ByteViewerComponentProvider.OPTION_EDIT_COLOR, Palette.GREEN);
final FieldLocation loc = getFieldLocation(getAddr(0x01001000)); final FieldLocation loc = getFieldLocation(getAddr(0x01001000));
SwingUtilities.invokeAndWait(() -> { SwingUtilities.invokeAndWait(() -> {
@ -1302,7 +1303,7 @@ public class ByteViewerPlugin2Test extends AbstractGhidraHeadedIntegrationTest {
program.flushEvents(); program.flushEvents();
ByteViewerComponent c = panel.getCurrentComponent(); ByteViewerComponent c = panel.getCurrentComponent();
ByteField field = c.getField(loc.getIndex(), loc.getFieldNum()); ByteField field = c.getField(loc.getIndex(), loc.getFieldNum());
assertEquals(Color.GREEN, field.getForeground()); assertEquals(Palette.GREEN, field.getForeground());
} }
@Test @Test
@ -1340,12 +1341,12 @@ public class ByteViewerPlugin2Test extends AbstractGhidraHeadedIntegrationTest {
Options opt = tool.getOptions("ByteViewer"); Options opt = tool.getOptions("ByteViewer");
// change the color for block separator // change the color for block separator
putColor(opt, ByteViewerComponentProvider.OPTION_SEPARATOR_COLOR, Color.GREEN); putColor(opt, ByteViewerComponentProvider.OPTION_SEPARATOR_COLOR, Palette.GREEN);
ByteViewerComponent c = panel.getCurrentComponent(); ByteViewerComponent c = panel.getCurrentComponent();
FieldLocation loc = getFieldLocation(getAddr(0x0f001000)); FieldLocation loc = getFieldLocation(getAddr(0x0f001000));
ByteField field = c.getField(loc.getIndex().subtract(BigInteger.ONE), 0); ByteField field = c.getField(loc.getIndex().subtract(BigInteger.ONE), 0);
assertEquals(Color.GREEN, field.getForeground()); assertEquals(Palette.GREEN, field.getForeground());
} }
@Test @Test

View file

@ -1,10 +1,12 @@
[Defaults] [Defaults]
color.bg.decompiler = color.bg color.bg.decompiler = color.bg
color.fg.decompiler = color.fg color.fg.decompiler = color.fg
color.fg.decompiler.keyword = #0001e6 color.fg.decompiler.keyword = #0001e6
color.fg.decompiler.function.name = blue color.fg.decompiler.function.name = blue
color.fg.decompiler.comment = blueViolet color.fg.decompiler.comment = blueViolet
color.fg.decompiler.error = crimson
color.fg.decompiler.variable = #999900 // close to oliveDrab color.fg.decompiler.variable = #999900 // close to oliveDrab
color.fg.decompiler.constant = forestGreen color.fg.decompiler.constant = forestGreen
color.fg.decompiler.type = mediumBlue color.fg.decompiler.type = mediumBlue
@ -37,6 +39,7 @@ color.bg.decompiler.pcode.dfg.edge.between.blocks = red
color.fg.decompiler.keyword = peru color.fg.decompiler.keyword = peru
color.fg.decompiler.function.name = cadetBlue color.fg.decompiler.function.name = cadetBlue
color.fg.decompiler.comment = lightSlateGray color.fg.decompiler.comment = lightSlateGray
color.fg.decompiler.error = crimson
color.fg.decompiler.variable = #999900 // close to oliveDrab color.fg.decompiler.variable = #999900 // close to oliveDrab
color.fg.decompiler.constant = forestGreen color.fg.decompiler.constant = forestGreen
color.fg.decompiler.type = blue color.fg.decompiler.type = blue

View file

@ -354,7 +354,7 @@ public class DecompileOptions {
private Color defaultSearchHighlightColor = SEARCH_HIGHLIGHT_DEF; private Color defaultSearchHighlightColor = SEARCH_HIGHLIGHT_DEF;
// Color applied to a token to indicate warning/error // Color applied to a token to indicate warning/error
private final static Color ERROR_COLOR = new Color(204, 0, 51); // Dark Red private final static Color ERROR_COLOR = new GColor("color.fg.decompiler.comment");
final static String FONT_MSG = "Display.Font"; final static String FONT_MSG = "Display.Font";
final static Font DEFAULT_FONT = new Font(Font.MONOSPACED, Font.PLAIN, 12); final static Font DEFAULT_FONT = new Font(Font.MONOSPACED, Font.PLAIN, 12);

View file

@ -436,7 +436,7 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest {
// The coloring algorithm: // The coloring algorithm:
// 1) If the grouped vertices are not colored, then use the default group color // 1) If the grouped vertices are not colored, then use the default group color
// 2) If the grouped vertices are colored, but not all the same color, // 2) If the grouped vertices are colored, but not all the same color,
// then use the default group color= // then use the default group color
// 3) If all grouped vertices share the same color, then make the group that color // 3) If all grouped vertices share the same color, then make the group that color
// //
// This test is for 1) // This test is for 1)
@ -472,7 +472,7 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest {
// The coloring algorithm: // The coloring algorithm:
// 1) If the grouped vertices are not colored, then use the default group color // 1) If the grouped vertices are not colored, then use the default group color
// 2) If the grouped vertices are colored, but not all the same color, // 2) If the grouped vertices are colored, but not all the same color,
// then use the default group color= // then use the default group color
// 3) If all grouped vertices share the same color, then make the group that color // 3) If all grouped vertices share the same color, then make the group that color
// //
// This test is for 2) // This test is for 2)
@ -514,7 +514,7 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest {
// The coloring algorithm: // The coloring algorithm:
// 1) If the grouped vertices are not colored, then use the default group color // 1) If the grouped vertices are not colored, then use the default group color
// 2) If the grouped vertices are colored, but not all the same color, // 2) If the grouped vertices are colored, but not all the same color,
// then use the default group color= // then use the default group color
// 3) If all grouped vertices share the same color, then make the group that color // 3) If all grouped vertices share the same color, then make the group that color
// //
// This test is for 3) // This test is for 3)

View file

@ -29,8 +29,8 @@ import javax.swing.border.LineBorder;
import docking.widgets.EmptyBorderButton; import docking.widgets.EmptyBorderButton;
import docking.widgets.label.GDLabel; import docking.widgets.label.GDLabel;
import generic.theme.GColor; import generic.theme.GColor;
import generic.theme.Gui;
import generic.theme.GThemeDefaults.Colors.Palette; import generic.theme.GThemeDefaults.Colors.Palette;
import generic.theme.Gui;
import ghidra.graph.viewer.vertex.AbstractVisualVertex; import ghidra.graph.viewer.vertex.AbstractVisualVertex;
import ghidra.graph.viewer.vertex.VertexShapeProvider; import ghidra.graph.viewer.vertex.VertexShapeProvider;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
@ -250,7 +250,7 @@ public class FcgVertex extends AbstractVisualVertex implements VertexShapeProvid
compactShape = (Double) vertexShape.clone(); compactShape = (Double) vertexShape.clone();
vertexImageLabel.setIcon(new ImageIcon(image)); vertexImageLabel.setIcon(new ImageIcon(image));
Border border = createDebugBorder(new LineBorder(Color.PINK, 1)); Border border = createDebugBorder(new LineBorder(Palette.PINK, 1));
vertexImageLabel.setBorder(border); vertexImageLabel.setBorder(border);
} }
@ -299,7 +299,7 @@ public class FcgVertex extends AbstractVisualVertex implements VertexShapeProvid
private void addNameLabel() { private void addNameLabel() {
Border border = createDebugBorder(new LineBorder(Color.GREEN, 1)); Border border = createDebugBorder(new LineBorder(Palette.GREEN, 1));
nameLabel.setBorder(border); nameLabel.setBorder(border);
// assume the vertex label has been bounded // assume the vertex label has been bounded

View file

@ -116,7 +116,7 @@ public class FunctionCallGraphPluginScreenShots extends GhidraScreenShotGenerato
area.width += (2 * offset); area.width += (2 * offset);
area.height += (2 * offset); area.height += (2 * offset);
// drawRectangle(Color.ORANGE, area, 5); // drawRectangle(Palette.ORANGE, area, 5);
crop(area); crop(area);
} }

View file

@ -30,6 +30,7 @@ import javax.swing.event.EventListenerList;
import com.google.common.collect.HashMultiset; import com.google.common.collect.HashMultiset;
import com.google.common.collect.Multiset; import com.google.common.collect.Multiset;
import generic.theme.GThemeDefaults.Colors;
import ghidra.service.graph.Attributed; import ghidra.service.graph.Attributed;
/** /**
@ -60,8 +61,7 @@ public class AttributeFilters implements ItemSelectable {
/** /**
* a {@link Function} to allow custom coloring of the individual toolkit button foreground * a {@link Function} to allow custom coloring of the individual toolkit button foreground
*/ */
private Function<String, Paint> paintFunction = v -> Color.black; private Function<String, Paint> paintFunction = v -> Colors.FOREGROUND;
/** /**
* @param excluded ignored keys * @param excluded ignored keys

View file

@ -53,6 +53,7 @@ import docking.options.editor.OptionsDialog;
import docking.widgets.EventTrigger; import docking.widgets.EventTrigger;
import docking.widgets.OptionDialog; import docking.widgets.OptionDialog;
import generic.theme.GColor; import generic.theme.GColor;
import generic.theme.GThemeDefaults.Colors;
import generic.util.WindowUtilities; import generic.util.WindowUtilities;
import ghidra.framework.options.Options; import ghidra.framework.options.Options;
import ghidra.framework.options.ToolOptions; import ghidra.framework.options.ToolOptions;
@ -1025,7 +1026,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
.elements(vertices) .elements(vertices)
.maxFactor(.05) .maxFactor(.05)
.buttonSupplier(JRadioButton::new) .buttonSupplier(JRadioButton::new)
.paintFunction(v -> Color.BLACK) .paintFunction(v -> Colors.FOREGROUND)
.build(); .build();
vertexFilters.addItemListener(item -> { vertexFilters.addItemListener(item -> {
@ -1043,7 +1044,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
.elements(edges) .elements(edges)
.maxFactor(.01) .maxFactor(.01)
.buttonSupplier(JRadioButton::new) .buttonSupplier(JRadioButton::new)
.paintFunction(e -> Color.BLACK) .paintFunction(e -> Colors.FOREGROUND)
.build(); .build();
edgeFilters.addItemListener(item -> { edgeFilters.addItemListener(item -> {

View file

@ -37,6 +37,7 @@ import org.jungrapht.visualization.renderers.Renderer;
import org.jungrapht.visualization.renderers.Renderer.VertexLabel.Position; import org.jungrapht.visualization.renderers.Renderer.VertexLabel.Position;
import org.jungrapht.visualization.util.RectangleUtils; import org.jungrapht.visualization.util.RectangleUtils;
import generic.theme.GThemeDefaults.Colors;
import generic.util.image.ImageUtils; import generic.util.image.ImageUtils;
import ghidra.service.graph.*; import ghidra.service.graph.*;
import ghidra.util.HTMLUtilities; import ghidra.util.HTMLUtilities;
@ -78,8 +79,8 @@ public class DefaultGraphRenderer implements GraphRenderer {
this.options = options; this.options = options;
renderingHints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); renderingHints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
label = new JLabel(); label = new JLabel();
label.setForeground(Color.black); label.setForeground(Colors.FOREGROUND);
label.setBackground(Color.white); label.setBackground(Colors.BACKGROUND);
label.setOpaque(false); label.setOpaque(false);
Border marginBorder = BorderFactory.createEmptyBorder(labelBorderSize, 2 * labelBorderSize, Border marginBorder = BorderFactory.createEmptyBorder(labelBorderSize, 2 * labelBorderSize,
labelBorderSize, 2 * labelBorderSize); labelBorderSize, 2 * labelBorderSize);
@ -163,7 +164,7 @@ public class DefaultGraphRenderer implements GraphRenderer {
} }
renderContext.setVertexFontFunction(this::getFont); renderContext.setVertexFontFunction(this::getFont);
renderContext.setVertexLabelRenderer(new JLabelVertexLabelRenderer(Color.black)); renderContext.setVertexLabelRenderer(new JLabelVertexLabelRenderer(Colors.FOREGROUND));
renderContext.setVertexDrawPaintFunction(this::getVertexColor); renderContext.setVertexDrawPaintFunction(this::getVertexColor);
renderContext.setVertexFillPaintFunction(this::getVertexColor); renderContext.setVertexFillPaintFunction(this::getVertexColor);
renderContext.setVertexStrokeFunction(n -> new BasicStroke(3.0f)); renderContext.setVertexStrokeFunction(n -> new BasicStroke(3.0f));
@ -290,7 +291,7 @@ public class DefaultGraphRenderer implements GraphRenderer {
// shapes are centered at the origin, so translate the graphics to compensate // shapes are centered at the origin, so translate the graphics to compensate
graphics.translate(-bounds.x + strokeThickness, -bounds.y + strokeThickness); graphics.translate(-bounds.x + strokeThickness, -bounds.y + strokeThickness);
graphics.setPaint(Color.WHITE); graphics.setPaint(Colors.BACKGROUND);
graphics.fill(scaledShape); graphics.fill(scaledShape);
graphics.setPaint(vertexColor); graphics.setPaint(vertexColor);
graphics.setStroke(new BasicStroke(strokeThickness)); graphics.setStroke(new BasicStroke(strokeThickness));
@ -304,7 +305,7 @@ public class DefaultGraphRenderer implements GraphRenderer {
int yOffset = (int) ((iconHeight - label.getHeight()) * labelOffsetRatio); int yOffset = (int) ((iconHeight - label.getHeight()) * labelOffsetRatio);
graphics.translate(xOffset, yOffset); graphics.translate(xOffset, yOffset);
graphics.setPaint(Color.black); graphics.setPaint(Colors.FOREGROUND);
label.paint(graphics); label.paint(graphics);
graphics.setTransform(graphicsTransform); // restore the original transform graphics.setTransform(graphicsTransform); // restore the original transform

View file

@ -42,6 +42,7 @@ import docking.widgets.label.GLabel;
import docking.widgets.textfield.HexOrDecimalInput; import docking.widgets.textfield.HexOrDecimalInput;
import docking.widgets.textfield.HintTextField; import docking.widgets.textfield.HintTextField;
import generic.theme.GThemeDefaults.Colors; import generic.theme.GThemeDefaults.Colors;
import generic.theme.GThemeDefaults.Colors.Messages;
import generic.theme.TempColorUtils; import generic.theme.TempColorUtils;
import ghidra.app.util.bin.format.pdb.PdbParser; import ghidra.app.util.bin.format.pdb.PdbParser;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbIdentifiers; import ghidra.app.util.bin.format.pdb2.pdbreader.PdbIdentifiers;
@ -910,7 +911,7 @@ public class LoadPdbDialog extends DialogComponentProvider {
} }
Graphics2D g2 = (Graphics2D) g; Graphics2D g2 = (Graphics2D) g;
g2.setColor(hintColor != null ? hintColor : Color.LIGHT_GRAY); g2.setColor(hintColor != null ? hintColor : Messages.HINT);
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
Dimension size = getSize(); Dimension size = getSize();

View file

@ -26,7 +26,6 @@ import javax.swing.text.*;
import javax.swing.tree.TreeSelectionModel; import javax.swing.tree.TreeSelectionModel;
import docking.DockingUtils; import docking.DockingUtils;
import docking.help.HelpService;
import docking.widgets.EventTrigger; import docking.widgets.EventTrigger;
import docking.widgets.OptionDialog; import docking.widgets.OptionDialog;
import docking.widgets.fieldpanel.FieldPanel; import docking.widgets.fieldpanel.FieldPanel;
@ -63,6 +62,7 @@ import ghidra.util.exception.CancelledException;
import ghidra.util.exception.VersionException; import ghidra.util.exception.VersionException;
import ghidra.util.task.*; import ghidra.util.task.*;
import help.Help; import help.Help;
import help.HelpService;
import resources.ResourceManager; import resources.ResourceManager;
/** /**

View file

@ -33,6 +33,7 @@ import docking.action.ToggleDockingAction;
import docking.tool.ToolConstants; import docking.tool.ToolConstants;
import docking.widgets.fieldpanel.FieldPanel; import docking.widgets.fieldpanel.FieldPanel;
import generic.test.AbstractGenericTest; import generic.test.AbstractGenericTest;
import generic.theme.GThemeDefaults.Colors.Palette;
import ghidra.app.events.ProgramLocationPluginEvent; import ghidra.app.events.ProgramLocationPluginEvent;
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin; import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
import ghidra.app.plugin.core.marker.MarkerManagerPlugin; import ghidra.app.plugin.core.marker.MarkerManagerPlugin;
@ -271,10 +272,10 @@ public class DiffTestAdapter extends AbstractGhidraHeadedIntegrationTest {
builder.setIntProperty("10018ff", "Space", 1); builder.setIntProperty("10018ff", "Space", 1);
builder.setIntProperty("100248c", "Space", 1); builder.setIntProperty("100248c", "Space", 1);
builder.setObjectProperty("100248c", "testColor", new SaveableColor(Color.CYAN)); builder.setObjectProperty("100248c", "testColor", new SaveableColor(Palette.CYAN));
builder.setObjectProperty("10039dd", "testColor", new SaveableColor(Color.BLACK)); builder.setObjectProperty("10039dd", "testColor", new SaveableColor(Palette.BLACK));
builder.setObjectProperty("10039f8", "testColor", new SaveableColor(Color.BLACK)); builder.setObjectProperty("10039f8", "testColor", new SaveableColor(Palette.BLACK));
builder.setObjectProperty("10039fe", "testColor", new SaveableColor(Color.RED)); builder.setObjectProperty("10039fe", "testColor", new SaveableColor(Palette.RED));
AbstractGenericTest.setInstanceField("recordChanges", builder.getProgram(), Boolean.TRUE); AbstractGenericTest.setInstanceField("recordChanges", builder.getProgram(), Boolean.TRUE);
@ -450,10 +451,10 @@ public class DiffTestAdapter extends AbstractGhidraHeadedIntegrationTest {
builder.setIntProperty("1002428", "Space", 1); builder.setIntProperty("1002428", "Space", 1);
builder.setIntProperty("100248c", "Space", 1); builder.setIntProperty("100248c", "Space", 1);
builder.setObjectProperty("100248c", "testColor", new SaveableColor(Color.WHITE)); builder.setObjectProperty("100248c", "testColor", new SaveableColor(Palette.WHITE));
builder.setObjectProperty("10039f1", "testColor", new SaveableColor(Color.BLACK)); builder.setObjectProperty("10039f1", "testColor", new SaveableColor(Palette.BLACK));
builder.setObjectProperty("10039f8", "testColor", new SaveableColor(Color.BLACK)); builder.setObjectProperty("10039f8", "testColor", new SaveableColor(Palette.BLACK));
builder.setObjectProperty("10039fe", "testColor", new SaveableColor(Color.GREEN)); builder.setObjectProperty("10039fe", "testColor", new SaveableColor(Palette.GREEN));
AbstractGenericTest.setInstanceField("recordChanges", builder.getProgram(), Boolean.TRUE); AbstractGenericTest.setInstanceField("recordChanges", builder.getProgram(), Boolean.TRUE);

View file

@ -2,6 +2,7 @@
##MODULE IP: Jython License ##MODULE IP: Jython License
##MODULE IP: LGPL 2.1 ##MODULE IP: LGPL 2.1
Module.manifest||GHIDRA||||END| Module.manifest||GHIDRA||||END|
data/python.theme.properties||GHIDRA||||END|
src/main/help/help/TOC_Source.xml||GHIDRA||||END| src/main/help/help/TOC_Source.xml||GHIDRA||||END|
src/main/help/help/topics/Python/images/erase16.png||GHIDRA||||END| src/main/help/help/topics/Python/images/erase16.png||GHIDRA||||END|
src/main/help/help/topics/Python/interpreter.html||GHIDRA||||END| src/main/help/help/topics/Python/interpreter.html||GHIDRA||||END|

View file

@ -0,0 +1,19 @@
[Defaults]
color.fg.plugin.python.syntax.class = blue
color.fg.plugin.python.syntax.code = darkgreen
color.fg.plugin.python.syntax.function = green
color.fg.plugin.python.syntax.instance = purple
color.fg.plugin.python.syntax.map = steelblue
color.fg.plugin.python.syntax.method = teal
color.fg.plugin.python.syntax.null = red
color.fg.plugin.python.syntax.number = darkgray
color.fg.plugin.python.syntax.package = darkred
color.fg.plugin.python.syntax.sequence = rbg(128, 96, 64)
color.fg.plugin.python.syntax.special = darkgreen
[Dark Defaults]

View file

@ -25,6 +25,7 @@ import org.python.core.PyInstance;
import org.python.core.PyObject; import org.python.core.PyObject;
import docking.widgets.label.GDLabel; import docking.widgets.label.GDLabel;
import generic.theme.GColor;
import ghidra.app.plugin.core.console.CodeCompletion; import ghidra.app.plugin.core.console.CodeCompletion;
import ghidra.framework.options.Options; import ghidra.framework.options.Options;
import ghidra.util.Msg; import ghidra.util.Msg;
@ -57,19 +58,21 @@ public class PythonCodeCompletionFactory {
private final static boolean INCLUDE_TYPES_DEFAULT = true; private final static boolean INCLUDE_TYPES_DEFAULT = true;
private static boolean includeTypes = INCLUDE_TYPES_DEFAULT; private static boolean includeTypes = INCLUDE_TYPES_DEFAULT;
public static final Color NULL_COLOR = new Color(255, 0, 0); //@formatter:off
public static final Color FUNCTION_COLOR = new Color(0, 128, 0); public static final Color NULL_COLOR = new GColor("color.fg.plugin.python.syntax.null");
public static final Color PACKAGE_COLOR = new Color(128, 0, 0); public static final Color FUNCTION_COLOR = new GColor("color.fg.plugin.python.syntax.function");
public static final Color CLASS_COLOR = new Color(0, 0, 255); public static final Color PACKAGE_COLOR = new GColor("color.fg.plugin.python.syntax.package");
public static final Color METHOD_COLOR = new Color(0, 128, 128); public static final Color CLASS_COLOR = new GColor("color.fg.plugin.python.syntax.class");
public static final Color METHOD_COLOR = new GColor("color.fg.plugin.python.syntax.method");
/* anonymous code chunks */ /* anonymous code chunks */
public static final Color CODE_COLOR = new Color(0, 64, 0); public static final Color CODE_COLOR = new GColor("color.fg.plugin.python.syntax.code");
public static final Color INSTANCE_COLOR = new Color(128, 0, 128); public static final Color INSTANCE_COLOR = new GColor("color.fg.plugin.python.syntax.instance");
public static final Color SEQUENCE_COLOR = new Color(128, 96, 64); public static final Color SEQUENCE_COLOR = new GColor("color.fg.plugin.python.syntax.sequence");
public static final Color MAP_COLOR = new Color(64, 96, 128); public static final Color MAP_COLOR = new GColor("color.fg.plugin.python.syntax.map");
public static final Color NUMBER_COLOR = new Color(64, 64, 64); public static final Color NUMBER_COLOR = new GColor("color.fg.plugin.python.syntax.number");
/* for weird Jython-specific stuff */ /* for weird Jython-specific stuff */
public static final Color SPECIAL_COLOR = new Color(64, 96, 64); public static final Color SPECIAL_COLOR = new GColor("color.fg.plugin.python.syntax.special");
//@formatter:on
static { static {
/* Order matters! This is the order in which classes are checked for /* Order matters! This is the order in which classes are checked for

View file

@ -9,21 +9,14 @@
##MODULE IP: Tango Icons - Public Domain ##MODULE IP: Tango Icons - Public Domain
Module.manifest||GHIDRA||||END| Module.manifest||GHIDRA||||END|
data/ExtensionPoint.manifest||GHIDRA||||END| data/ExtensionPoint.manifest||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|
src/main/help/help/shared/menu16.gif||GHIDRA||||END|
src/main/help/help/shared/note-red.png||Oxygen Icons - LGPL 3.0|||Oxygen icon theme (dual license; LGPL or CC-SA-3.0)|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|
src/main/help/help/shared/note.yellow.png||Oxygen Icons - LGPL 3.0|||Oxygen icon theme (dual license; LGPL or CC-SA-3.0)|END|
src/main/help/help/shared/redo.png||GHIDRA||||END|
src/main/help/help/shared/tip.png||Oxygen Icons - LGPL 3.0|||Oxygen icon theme (dual license; LGPL or CC-SA-3.0)|END|
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.material.theme.properties||GHIDRA||||END|
data/docking.palette.theme.properties||GHIDRA||||END| data/docking.palette.theme.properties||GHIDRA||||END|
data/docking.theme.properties||GHIDRA||||END| data/docking.theme.properties||GHIDRA||||END|
src/main/help/help/TOC_Source.xml||GHIDRA||||END|
src/main/help/help/topics/Theming/Theming.htm||GHIDRA||||END|
src/main/help/help/topics/Theming/images/ColorEditor.png||GHIDRA||||END|
src/main/help/help/topics/Theming/images/FontEditor.png||GHIDRA||||END|
src/main/help/help/topics/Theming/images/IconEditor.png||GHIDRA||||END|
src/main/help/help/topics/Theming/images/ThemeDialog.png||GHIDRA||||END|
src/main/java/docking/dnd/package.html||GHIDRA||reviewed||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/options/editor/package.html||GHIDRA||reviewed||END|
src/main/java/docking/widgets/fieldpanel/package.html||GHIDRA||reviewed||END| src/main/java/docking/widgets/fieldpanel/package.html||GHIDRA||reviewed||END|

View file

@ -6,17 +6,21 @@ color.palette.disabled = rgba(255,255,255,0)
color.palette.black = black color.palette.black = black
color.palette.blue = blue color.palette.blue = blue
color.palate.cornflowerblue = cornflowerblue color.palate.cornflowerblue = cornflowerblue
color.palate.cornsilk = cornsilk
color.palette.crimson = crimson color.palette.crimson = crimson
color.palette.cyan = cyan color.palette.cyan = cyan
color.palette.darkblue = DarkBlue color.palette.darkblue = DarkBlue
color.palette.darkgray = DarkGray color.palette.darkgray = DarkGray
color.palette.darkgreen = darkgreen
color.palette.darkkhaki = DarkKhaki color.palette.darkkhaki = DarkKhaki
color.palette.darkred = DarkRed color.palette.darkred = DarkRed
color.palette.darkslategray = darkslategray color.palette.darkslategray = darkslategray
color.palette.dodgerblue = DodgerBlue color.palette.dodgerblue = DodgerBlue
color.palette.fuchsia = fuchsia
color.palette.gold = gold color.palette.gold = gold
color.palette.gray = gray color.palette.gray = gray
color.palette.green = green color.palette.green = green
color.palette.greenyellow = greenyellow
color.palette.indigo = indigo color.palette.indigo = indigo
color.palette.khaki = khaki color.palette.khaki = khaki
color.palette.lavender = lavender color.palette.lavender = lavender
@ -36,10 +40,14 @@ color.palette.olive = olive
color.palette.orange = orange color.palette.orange = orange
color.palette.palegreen = palegreen color.palette.palegreen = palegreen
color.palette.palevioletred = PaleVioletRed color.palette.palevioletred = PaleVioletRed
color.palette.peachpuff = peachpuff
color.palette.pink = pink color.palette.pink = pink
color.palette.purple = purple color.palette.purple = purple
color.palette.red = red color.palette.red = red
color.palette.silver = silver color.palette.silver = silver
color.palette.steelblue = steelblue
color.palette.tan = tan
color.palette.teal = teal
color.palette.yellow = yellow color.palette.yellow = yellow
color.palette.yellowgreen = yellowgreen color.palette.yellowgreen = yellowgreen
color.palette.white = white color.palette.white = white

View file

@ -73,7 +73,7 @@ color.bg.tree.drag.no.selection = rgb(204, 204, 255)
color.bg.filterfield = color.bg.filtered color.bg.filterfield = color.bg.filtered
color.fg.filterfield = black color.fg.filterfield = black
color.bg.selection.help = lightSteelBlue color.bg.selection.help = lightsteelblue
// generic component items // generic component items
color.border.bevel.highlight = lightGray color.border.bevel.highlight = lightGray

View file

@ -20,9 +20,9 @@
During the help build time, all TOC_Source.xml files will be parsed and validated to ensure During the help build time, all TOC_Source.xml files will be parsed and validated to ensure
that all <tocref> tags point to valid <tocdef> tags. From these files will be generated that all <tocref> tags point to valid <tocdef> tags. From these files will be generated
<module name>_TOC.xml files, which are table of contents files written in the format <module name>_TOC.xml files, which are table of contents files written in the format
desired by the JavaHelp system. Additionally, the genated files will be merged together desired by the JavaHelp system. Additionally, the generated files will be merged together
as they are loaded by the JavaHelp system. In the end, when displaying help in the Ghidra as they are loaded by the JavaHelp system. In the end, when displaying help in the Ghidra
help GUI, there will be on table of contents that has been created from the definitions in help GUI, there will be one table of contents that has been created from the definitions in
all of the modules' TOC_Source.xml files. all of the modules' TOC_Source.xml files.
@ -49,6 +49,8 @@
<tocroot> <tocroot>
<tocdef id="Theming"
text="Theming"
target="help/topics/Theming/Theming.htm" >
</tocdef>
</tocroot> </tocroot>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 859 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 187 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 185 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -1,17 +0,0 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN">
<HTML>
<HEAD>
<TITLE>Placholder Title</TITLE>
<LINK rel="stylesheet" type="text/css" href="../../shared/Frontpage.css">
</HEAD>
<BODY>
<H1>Stub</H1>
<P>Blah blah</P>
<P class="relatedtopic">Related Topics:</P>
</BODY>
</HTML>

View file

@ -0,0 +1,302 @@
<!DOCTYPE doctype>
<HTML>
<HEAD>
<META name="generator" content=
"HTML Tidy for Java (vers. 2009-12-01), see jtidy.sourceforge.net">
<TITLE>Theming</TITLE>
<LINK rel="stylesheet" type="text/css" href="../../shared/Frontpage.css">
</HEAD>
<BODY>
<H1 align="center">Theming</H1>
<H2>Overview</H2>
<P>The Theming feature allows users to customize the colors, fonts, and icons used throughout
the application. The active theme determines the Java LookAndFeel, whether the theme should use
light or dark defaults, and any custom colors, fonts, and icons that override the default
values. Users can can easily switch between any of the built-in themes or any saved themes from
their home application directory</P>
<P>Users can also edit and create their own themes using the Theme Editor. Custom themes are
stored in the users &lt;application dir&gt;/themes. These are simple text files that can also
easily be modified using any text editor. Also, users can share themes by exporting them to a
file that can be given to other users who can them import them into their own system.</P>
<H2>Theme Dialog<A name="Edit_Theme"></A></H2>
<P>The Theme Dialog is the primary means for creating, editing, and saving themes.</P>
<P align="center"><IMG alt="" src="images/ThemeDialog.png"><BR>
&nbsp;</P>
<P>The Theme Dialog consists of a theme comboBox and a tabbed set of tables that display the
values for every color property, font property, and icon property defined by either the Java
LookAndFeel or the Ghidra application. All Ghidra defined properties start with "color.",
"font.", or "icon." depending on whether the the property is a color, font, or icon
respectively. All other properties are defined by the Java LookAndFeel.</P>
<P>Each table entry shows the property id string and then the current value, the theme value,
and the default color. Most often, the three values are equal. If the theme value is different
from the default value, that means that the active theme has overridden the default value for
that property. If the current value is different from the theme value, that means the user has
changed that value, but not yet saved the changes back to the theme.</P>
<P>Individual values can be changed by double clicking the Id or Current Color column. This
will bring up an appropriate editor for changing the value. When editing a value, the change
takes place immediately in the application so you can see the effect. When you leave the
specific property editor, you have the choice of keeping the change or canceling and having it
revert back to its previous value.</P>
<P>If any values have been changed, the "Save" button will become enabled, allowing you to save
any changes you have made. (Hitting "Dismiss" will also give the the option to save.) If the
current theme is a built-in theme, you will first have to supply a new theme name. If the
current theme is a not a built-in theme, you will have the option to overwrite the existing
theme or supplying a new name to save it as a new theme.</P>
<BLOCKQUOTE>
<H3>Color Editor</H3>
<P>When you double-click on a color value, the Edit Color dialog appears.</P>
<P align="center"><IMG alt="" src="images/ColorEditor.png"><BR>
&nbsp;</P>
<P>Any change you make in the editor is applied to the application immediately. If you press
the OK button, the change will stay. If you press the Cancel button, the color will revert
back to the original color.</P>
<H3>Font Editor</H3>
<P>When you double-click on a font value, the Edit Font dialog appears.</P>
<P align="center"><IMG alt="" src="images/FontEditor.png"><BR>
&nbsp;</P>
<P>Any change you make in the editor is applied to the application immediately. If you press
the OK button, the change will stay. If you press the Cancel button, the font will revert
back to the original font.</P>
<H3>Icon Editor</H3>
<P>When you double-click on an Icon value, the Edit Icon dialog appears.</P>
<P align="center"><IMG alt="" src="images/IconEditor.png"><BR>
&nbsp;</P>
<P>The Edit Icon dialog has a drop down text field where you can find any existing icon on
the classpath. If you want to choose an Icon from the file system, press the "..." button and
a FileChooser will appear, allowing you to pick an icon file from anywhere in the filesystem.
Any change you make in the editor is applied to the application immediately. If you press the
OK button, the change will stay. If you press the Cancel button, the icon will revert back to
the original icon.</P>
</BLOCKQUOTE>
<H2>Theme Actions</H2>
<BLOCKQUOTE>
<H3>Switching Themes</H3>
<P>To change the current theme, first bring up the <A href="#Edit_Theme">Theme Dialog</A>.
The Theme Dialog can be invoked from the Ghidra Project Window menu using the
<B>Edit</B><IMG alt="" src="images/arrow.gif" border="0"><B>Theme</B> menu. Then select
a theme from the combo box at the top of the Theme Editor dialog.</P>
<H3>Modifying Theme Values</H3>
<P>All the colors, fonts, and icons that have been externalized can be modified using the <A
href="#Edit_Theme">Theme Dialog</A>. The Theme Dialog can be invoked from the
Ghidra Project Window using the
<B>Edit</B><IMG alt="" src="images/arrow.gif" border="0"><B>Theme</B> menu. Choose the
tab for the appropriate type and double-click on the id column or current value column of the
item you want to change. An editor for that type will appear.</P>
<H3>Reseting Theme Values</H3>
<P>To reset an individual value back to its original theme value, invoke the <A href=
"#Edit_Theme">Theme Dialog</A> using the <B>Edit</B> <IMG alt="" src="images/arrow.gif"
border="0"><B>Theme</B> menu. Switch to the appropriate tab for either colors, fonts, or
icons. Right-click on the row of the value you want to reset and choose the <B>Restore
Value</B> menu item.</P>
<H3>Reseting All Theme Values<A name="Reset_Theme_Values"></A></H3>
<P>To reset all values back to the original values established by the current theme, invoked
the <B>Edit</B><IMG alt="" src="images/arrow.gif" border="0"><B>Theme Actions</B> <IMG
alt="" src="images/arrow.gif" border="0"><B>Reset Theme Values</B> menu.</P>
<H3>Saving Themes</H3>
<P>After making changes to one or more theme values, the <A href="#Edit_Theme">Theme
Dialog's</A> <B>Save</B> button will be enabled. Pressing the <B>Save</B> button will give
the user the option of creating a new theme or overwriting the current them (if the current
theme is not a built-in theme). Also, users will have the options of saving a theme if they
dismiss the Theme Dialog while there are changes to one or more theme values.</P>
<H3>Deleting Themes<A name="Delete_Theme"></A></H3>
<P>To delete a custom saved theme, invoked the <B>Edit</B><IMG alt="" src=
"images/arrow.gif" border="0"><B>Theme Actions</B> <IMG alt="" src=
"images/arrow.gif" border="0"><B>Delete Theme...</B> menu. This will bring up a dialog
with a list of themes that can be deleted. Select the theme to delete and press the <B>Ok</B>
button.</P>
<H3>Exporting Themes&gt;<A name="Export_Theme"></A></H3>
<P>To export a theme so that it can be shared with others, invoke the <B>Edit</B> <IMG alt=""
src="images/arrow.gif" border="0"><B>Theme Actions</B> <IMG alt="" src=
"images/arrow.gif" border="0"><B>Export Theme...</B> menu. You will first be asked if
you want to export as a regular theme file or as a Zip file. The Zip file option is useful if
the current theme has icon values that are not included with standard Ghidra. In that case,
the Zip file will include those non standard icon files.</P>
<H3>Importing Themes<A name="Import_Theme"></A></H3>
<P>To import a theme, invoke the <B>Edit</B> <IMG alt="" src="images/arrow.gif" border=
"0"><B>Theme Actions</B> <IMG alt="" src="images/arrow.gif" border="0"><B>Import
Theme...</B> menu. A file chooser dialog will appear allowing the user to choose a theme file
to import. The selected file can be either a standard theme file or a Zip file containing the
theme file and any non-standard icon files defined by that theme.</P>
<H3>Reloading Default Values</H3>
<P>XThis action causes Ghidra to reload all theme default values. It is really only useful
for developers who are actively making changes to theme.properties files. To activate this
action, press the refresh button <IMG alt="" src="images/reload3.png" border="0"> in the top
right corner of the <A href="#Edit_Theme">Theme Dialog</A>.</P>
</BLOCKQUOTE>
<H2>Theme Property Names</H2>
<P>Theme Property Names (also referred to as ids or keys) that are defined by Ghidra use a
common format to help make sorting and viewing properties more intuitive as to their use.</P>
<P>The general format is:</P>
<BLOCKQUOTE>
<BLOCKQUOTE>
[type].[category[...]].[client].[specialized uses]
<UL>
<LI><B>type:</B> color, font, or icon</LI>
<LI><B>category:</B> any value, examples include "bg" (background), "fg" (foreground);
this may be multiple separated values</LI>
<LI><B>client:</B> the plugin name or feature name; any value used to represent a more
specialized use</LI>
<LI><B>specialized uses:</B> any key value here that applies to the client, such as
"vertex" for a vertex background</LI>
</UL>Examples:
<UL>
<LI>color.bg</LI>
<LI>color.bg.listing</LI>
<LI>font.button</LI>
<LI>icon.refresh</LI>
<LI>icon.refresh.disabled</LI>
</UL>
</BLOCKQUOTE>
</BLOCKQUOTE>
<H2>Theme File Format</H2>
<BLOCKQUOTE>
<P>Saved theme files have the following format:</P>
<PRE>
<CODE class="code">
name = ThemeName
lookAndFeel = Nimbus
useDarkDefaults = false
color.bg = Black
color.bg.foo = #012345
[color]Panel.background = Red
font.button = dialog-PLAIN-14
icon.refresh = images/reload3.png
color.bg.bar = color.bg.foo
color.bg.xxx = [color]Panel.background
</CODE>
</PRE>
<P>The first three properties are always the theme name, the look and feel name, and whether
the theme uses standard defaults or dark defaults. Then there is just a list of overridden
property "name = value" lines.</P>
<P>Each property line is expected to begin with either "color.", "font.", or "icon." Since
java defined properties don't start with these prefixes, they will have "[color]", "[font]",
or "[icon]" prepended to their property name. These are just there for the purposes of
parsing this file. When the properties are used in Ghidra, those bracketed prefixes are
removed.</P>
<P>Also, note that the values of these properties can reference other property names. If the
right side of the assignment is a property name and not a value, then it must also use the
bracketed prefix if the property name doesn't start with "color.", "font.", or "icon."</P>
<H3>Specifying Color Values</H3>
<P>To specify the value for a color, there are 3 acceptable formats:</P>
<UL>
<LI><B>Web Color Name</B> - Examples: Blue, Red, Black, etc.</LI>
<LI><B>Hex value</B> - #rrggbb, Example: #ff01a4, where ff is the red value, 01 is the
green value, and a4 is the blue value</LI>
<LI><B>Hex with alpha value</B> -Example: #ff01a480, where ff is the red value, 01 is the
green value, and a4 is the blue value, and 80 is the alpha value</LI>
<LI><B>RGB values</B> - Example: rgb(12, 34, 56) where red is 12(decimal), green is 34, and
blue is 56</LI>
<LI><B>RGBA values</B> - Eample: rgba(12, 34, 56, 127) where red is 12(decimal), green is
34, blue is 56, and alpha is 127</LI>
</UL>
<H3>Specifying Font Values</H3>
<P>Font values are specified as follows:</P>
<PRE>
familyName-style-size
</PRE>
<UL>
<LI><B>familyName:</B> the font family name such as "monospace", "dialog", "courier"</LI>
<LI><B>style:</B>either PLAIN, BOLD, ITALIC, or BOLDITALIC</LI>
<LI><B>size:</B> the font size such as 12, 14, 22</LI>
</UL>
<P>Examples: monospace-PLAIN-12 or courier-BOLD-15</P>
<H3>Specifying Icon Values</H3>
<P>Icon values are specified by a relative path to the icon file. There are two types of
icons; those that are included with Ghidra and those that were selected from the filesystem
and imported into the theme. Icons that have been imported into the theme are stored in an
images directory in the users Ghidra application directory. Icons included with Ghidra are
relative to an images data directory in some module. So for example,</P>
<PRE>
icon.refresh = images/view-refresh.png
icon.push = [EXTERNAL]images/energy.png
</PRE>
<P>The first example is a standard icon included with Ghidra. It exists in a module's data
directory in the images folder. The second example is for an icon that is not included with
Ghidra. The "[EXTERNAL]" prefix indicates that this icon is being stored in the user's
application directory in the images folder.</P>
</BLOCKQUOTE>
</BODY>
</HTML>

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

View file

@ -1,118 +0,0 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package docking;
import java.awt.*;
import javax.swing.JButton;
import docking.help.HelpDescriptor;
import docking.help.HelpService;
import ghidra.util.HelpLocation;
import ghidra.util.Msg;
public class DefaultHelpService implements HelpService {
@Override
public void showHelp(Object helpObj, boolean infoOnly, Component parent) {
if (infoOnly) {
displayHelpInfo(helpObj);
return;
}
}
@Override
public void showHelp(java.net.URL url) {
// no-op
}
@Override
public void showHelp(HelpLocation location) {
// no-op
}
@Override
public void excludeFromHelp(Object helpObject) {
// no-op
}
@Override
public boolean isExcludedFromHelp(Object helpObject) {
return false;
}
@Override
public void clearHelp(Object helpObject) {
// no-op
}
@Override
public void registerHelp(Object helpObj, HelpLocation helpLocation) {
// no-op
}
@Override
public HelpLocation getHelpLocation(Object object) {
return null;
}
@Override
public boolean helpExists() {
return false;
}
@Override
public void reload() {
// no-op
}
private void displayHelpInfo(Object helpObj) {
String msg = getHelpInfo(helpObj);
Msg.showInfo(this, null, "Help Info", msg);
}
private String getHelpInfo(Object helpObj) {
if (helpObj == null) {
return "Help Object is null";
}
StringBuilder buffy = new StringBuilder();
buffy.append("HELP OBJECT: " + helpObj.getClass().getName());
buffy.append("\n");
if (helpObj instanceof HelpDescriptor) {
HelpDescriptor helpDescriptor = (HelpDescriptor) helpObj;
buffy.append(helpDescriptor.getHelpInfo());
}
else if (helpObj instanceof JButton) {
JButton button = (JButton) helpObj;
buffy.append(" BUTTON: " + button.getText());
buffy.append("\n");
Component c = button;
while (c != null && !(c instanceof Window)) {
c = c.getParent();
}
if (c instanceof Dialog) {
buffy.append(" DIALOG: " + ((Dialog) c).getTitle());
buffy.append("\n");
}
if (c instanceof Frame) {
buffy.append(" FRAME: " + ((Frame) c).getTitle());
buffy.append("\n");
}
}
return buffy.toString();
}
}

View file

@ -1,527 +0,0 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package docking.help;
import java.awt.Component;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Hashtable;
import java.util.Locale;
import javax.help.*;
import javax.help.Map.ID;
import javax.help.event.HelpModelEvent;
import javax.help.plaf.HelpNavigatorUI;
import javax.help.plaf.basic.BasicTOCCellRenderer;
import javax.help.plaf.basic.BasicTOCNavigatorUI;
import javax.swing.*;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreePath;
import ghidra.util.Msg;
import ghidra.util.SystemUtilities;
/**
* A custom Table of Contents view that we specify in our JavaHelp xml documents. This view
* lets us install custom renderers and custom tree items for use by those renderers. These
* renderers let us display custom text defined by the TOC_Source.xml files. We also add some
* utility like: tooltips in development mode, node selection when pressing F1.
*/
public class CustomTOCView extends TOCView {
private CustomTOCNavigatorUI ui;
private boolean isSelectingNodeInternally;
// Hashtable
public CustomTOCView(HelpSet hs, String name, String label,
@SuppressWarnings("rawtypes") Hashtable params) {
this(hs, name, label, hs.getLocale(), params);
}
// Hashtable
public CustomTOCView(HelpSet hs, String name, String label, Locale locale,
@SuppressWarnings("rawtypes") Hashtable params) {
super(hs, name, label, locale, params);
}
@Override
// overrode this method to install our custom UI, which lets us use our custom renderer
public Component createNavigator(HelpModel model) {
JHelpTOCNavigator helpTOCNavigator = new JHelpTOCNavigator(this, model) {
@Override
public void setUI(HelpNavigatorUI newUI) {
CustomTOCView.this.ui = new CustomTOCNavigatorUI(this);
super.setUI(CustomTOCView.this.ui);
}
};
return helpTOCNavigator;
}
public HelpModel getHelpModel() {
if (ui == null) {
return null;
}
return ui.getHelpModel();
}
@Override
// overrode this method to install our custom factory
public DefaultMutableTreeNode getDataAsTree() {
DefaultMutableTreeNode superNode = super.getDataAsTree();
if (superNode.getChildCount() == 0) {
return superNode; // something is not initialized
}
@SuppressWarnings("rawtypes")
Hashtable viewParameters = getParameters();
String TOCData = (String) viewParameters.get("data");
HelpSet helpSet = getHelpSet();
URL helpSetURL = helpSet.getHelpSetURL();
URL url;
try {
url = new URL(helpSetURL, TOCData);
}
catch (Exception ex) {
throw new Error("Unable to create tree for view data: " + ex);
}
return parse(url, helpSet, helpSet.getLocale(), new CustomDefaultTOCFactory(), this);
}
/**
* Our custom factory that knows how to look for extra XML attributes and how to
* create our custom tree items
*/
public static class CustomDefaultTOCFactory extends DefaultTOCFactory {
@Override
public TreeItem createItem(String tagName, @SuppressWarnings("rawtypes") Hashtable atts,
HelpSet hs, Locale locale) {
try {
return doCreateItem(tagName, atts, hs, locale);
}
catch (Exception e) {
Msg.error(this, "Unexected error creating a TOC item", e);
throw new RuntimeException("Unexpected error creating a TOC item", e);
}
}
private TreeItem doCreateItem(String tagName, @SuppressWarnings("rawtypes") Hashtable atts,
HelpSet hs, Locale locale) {
TreeItem item = super.createItem(tagName, atts, hs, locale);
CustomTreeItemDecorator newItem = new CustomTreeItemDecorator((TOCItem) item);
if (atts != null) {
String displayText = (String) atts.get("display");
newItem.setDisplayText(displayText);
String tocID = (String) atts.get("toc_id");
newItem.setTocID(tocID);
}
return newItem;
}
}
/**
* Our hook to install our custom cell renderer.
*/
class CustomTOCNavigatorUI extends BasicTOCNavigatorUI {
public CustomTOCNavigatorUI(JHelpTOCNavigator b) {
super(b);
}
@Override
public void installUI(JComponent c) {
super.installUI(c);
tree.setExpandsSelectedPaths(true);
}
@Override
protected void setCellRenderer(NavigatorView view, JTree tree) {
Map map = view.getHelpSet().getCombinedMap();
tree.setCellRenderer(new CustomCellRenderer(map, (TOCView) view));
ToolTipManager.sharedInstance().registerComponent(tree);
}
public HelpModel getHelpModel() {
JHelpNavigator helpNavigator = getHelpNavigator();
return helpNavigator.getModel();
}
// Overridden to change the value used for the 'historyName', which we want to be our
// display name and not the item's name
@Override
public void valueChanged(TreeSelectionEvent e) {
if (isSelectingNodeInternally) {
// ignore our own selection events, as this method will get called twice if we don't
return;
}
JHelpNavigator navigator = getHelpNavigator();
HelpModel helpModel = navigator.getModel();
TreeItem treeItem = getSelectedItem(e, navigator);
if (treeItem == null) {
return; // nothing selected
}
TOCItem item = (TOCItem) treeItem;
ID itemID = item.getID();
if (itemID == null) {
Msg.debug(this, "No help ID for " + item);
return;
}
String presentation = item.getPresentation();
if (presentation != null) {
return; // don't currently support presentations
}
CustomTreeItemDecorator customItem = (CustomTreeItemDecorator) item;
String customDisplayText = customItem.getDisplayText();
try {
helpModel.setCurrentID(itemID, customDisplayText, navigator);
}
catch (InvalidHelpSetContextException ex) {
Msg.error(this, "Exception setting new help item ID", ex);
}
}
private TOCItem getSelectedItem(TreeSelectionEvent e, JHelpNavigator navigator) {
TreePath newLeadSelectionPath = e.getNewLeadSelectionPath();
if (newLeadSelectionPath == null) {
navigator.setSelectedItems(null);
return null;
}
DefaultMutableTreeNode node =
(DefaultMutableTreeNode) newLeadSelectionPath.getLastPathComponent();
TOCItem treeItem = (TOCItem) node.getUserObject();
navigator.setSelectedItems(new TreeItem[] { treeItem });
return treeItem;
}
// Overridden to try to find a parent file for IDs that are based upon anchors within
// a file
@Override
public synchronized void idChanged(HelpModelEvent e) {
selectNodeForID(e.getURL(), e.getID());
}
private void selectNodeForID(URL url, ID ID) {
if (ID == null) {
ID = getClosestID(url);
}
TreePath path = tree.getSelectionPath();
if (isAlreadySelected(path, ID)) {
return;
}
DefaultMutableTreeNode node = getNodeForID(topNode, ID);
if (node != null) {
isSelectingNodeInternally = true;
TreePath newPath = new TreePath(node.getPath());
tree.setSelectionPath(newPath);
tree.scrollPathToVisible(newPath);
isSelectingNodeInternally = false;
return;
}
// See if the given ID is based upon a URL with an anchor. If that is the case, then
// there may be a node for the parent file of that URL. In that case, select the
// parent file.
if (url == null) {
clearSelection();
return;
}
String urlString = url.toExternalForm();
int anchorIndex = urlString.indexOf('#');
if (anchorIndex < 0) {
clearSelection();
return;
}
urlString = urlString.substring(0, anchorIndex);
try {
URL newURL = new URL(urlString);
selectNodeForID(newURL, null);
}
catch (MalformedURLException e) {
// shouldn't happen, as we are starting with a valid URL
Msg.debug(this,
"Unexpected error create a help URL from an existing URL: " + urlString, e);
}
}
private ID getClosestID(URL url) {
HelpModel helpModel = toc.getModel();
HelpSet helpSet = helpModel.getHelpSet();
Map combinedMap = helpSet.getCombinedMap();
return combinedMap.getClosestID(url);
}
private boolean isAlreadySelected(TreePath path, ID id) {
if (path == null) {
return false;
}
Object pathComponent = path.getLastPathComponent();
if (!(pathComponent instanceof DefaultMutableTreeNode)) {
return false;
}
DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) pathComponent;
TOCItem item = (TOCItem) treeNode.getUserObject();
if (item == null) {
return false;
}
ID selectedID = item.getID();
return selectedID != null && selectedID.equals(id);
}
private DefaultMutableTreeNode getNodeForID(DefaultMutableTreeNode node, ID ID) {
if (ID == null) {
return null;
}
if (isNodeID(node, ID)) {
return node;
}
int childCount = node.getChildCount();
for (int i = 0; i < childCount; i++) {
DefaultMutableTreeNode matchingNode =
getNodeForID((DefaultMutableTreeNode) node.getChildAt(i), ID);
if (matchingNode != null) {
return matchingNode;
}
}
return null;
}
private boolean isNodeID(DefaultMutableTreeNode node, ID ID) {
Object userObject = node.getUserObject();
if (!(userObject instanceof TOCItem)) {
return false;
}
TOCItem item = (TOCItem) userObject;
ID nodeID = item.getID();
if (nodeID == null) {
return false;
}
return nodeID.equals(ID);
}
private void clearSelection() {
isSelectingNodeInternally = true;
tree.clearSelection();
isSelectingNodeInternally = false;
}
}
static class CustomCellRenderer extends BasicTOCCellRenderer {
public CustomCellRenderer(Map map, TOCView view) {
super(map, view);
}
@Override
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel,
boolean expanded, boolean leaf, int row, boolean isFocused) {
CustomCellRenderer renderer =
(CustomCellRenderer) super.getTreeCellRendererComponent(tree, value, sel, expanded,
leaf, row, isFocused);
TOCItem item = (TOCItem) ((DefaultMutableTreeNode) value).getUserObject();
if (item == null) {
return renderer;
}
CustomTreeItemDecorator customItem = (CustomTreeItemDecorator) item;
renderer.setText(customItem.getDisplayText());
if (!SystemUtilities.isInDevelopmentMode()) {
return renderer;
}
URL url = customItem.getURL();
if (url != null) {
renderer.setToolTipText(url.toExternalForm());
return renderer;
}
ID id = customItem.getID();
if (id != null) {
renderer.setToolTipText("Missing Help - " + id.id + " in '" + id.hs + "' help set");
return renderer;
}
// this can happen if there is no 'target' attribute in the TOC
// (see TOCView.createItem())
return renderer;
}
}
/**
* A custom tree item that allows us to store and retrieve custom attributes that we parsed
* from the TOC xml document.
*/
public static class CustomTreeItemDecorator extends javax.help.TOCItem {
private final TOCItem wrappedItem;
private String displayText;
private String tocID;
private URL cachedURL;
public CustomTreeItemDecorator(javax.help.TOCItem wrappedItem) {
super(wrappedItem.getID(), wrappedItem.getImageID(), wrappedItem.getHelpSet(),
wrappedItem.getLocale());
this.wrappedItem = wrappedItem;
}
void setDisplayText(String text) {
this.displayText = text;
}
public String getDisplayText() {
return displayText;
}
void setTocID(String tocID) {
this.tocID = tocID;
}
public String getTocID() {
return tocID;
}
@Override
public boolean equals(Object obj) {
return wrappedItem.equals(obj);
}
@Override
public int getExpansionType() {
return wrappedItem.getExpansionType();
}
@Override
public HelpSet getHelpSet() {
return wrappedItem.getHelpSet();
}
@Override
public ID getID() {
return wrappedItem.getID();
}
@Override
public ID getImageID() {
return wrappedItem.getImageID();
}
@Override
public Locale getLocale() {
return wrappedItem.getLocale();
}
@Override
public String getMergeType() {
return wrappedItem.getMergeType();
}
@Override
public String getName() {
return wrappedItem.getName();
}
@Override
public String getPresentation() {
return wrappedItem.getPresentation();
}
@Override
public String getPresentationName() {
return wrappedItem.getPresentationName();
}
@Override
public URL getURL() {
if (cachedURL == null) {
cachedURL = wrappedItem.getURL();
}
return cachedURL;
}
@Override
public int hashCode() {
return wrappedItem.hashCode();
}
@Override
public void setExpansionType(int type) {
wrappedItem.setExpansionType(type);
}
@Override
public void setHelpSet(HelpSet hs) {
wrappedItem.setHelpSet(hs);
}
@Override
public void setID(ID id) {
wrappedItem.setID(id);
}
@Override
public void setMergeType(String mergeType) {
wrappedItem.setMergeType(mergeType);
}
@Override
public void setName(String name) {
wrappedItem.setName(name);
}
@Override
public void setPresentation(String presentation) {
wrappedItem.setPresentation(presentation);
}
@Override
public void setPresentationName(String presentationName) {
wrappedItem.setPresentationName(presentationName);
}
@Override
public String toString() {
return displayText;
}
}
}

View file

@ -37,6 +37,7 @@ import org.jdesktop.animation.timing.TimingTargetAdapter;
import docking.framework.ApplicationInformationDisplayFactory; import docking.framework.ApplicationInformationDisplayFactory;
import docking.util.AnimationPainter; import docking.util.AnimationPainter;
import docking.util.AnimationUtils; import docking.util.AnimationUtils;
import generic.theme.GColor;
import ghidra.framework.preferences.Preferences; import ghidra.framework.preferences.Preferences;
import ghidra.util.Msg; import ghidra.util.Msg;
import ghidra.util.Swing; import ghidra.util.Swing;
@ -398,7 +399,7 @@ public class DockingHelpBroker extends GHelpBroker {
private class LocationHintPainter implements AnimationPainter { private class LocationHintPainter implements AnimationPainter {
private Color color = new Color(100, 100, 255, 100); private Color color = new GColor("color.bg.help.hint");
private Shape paintShape; private Shape paintShape;
LocationHintPainter(Shape paintShape) { LocationHintPainter(Shape paintShape) {
@ -450,11 +451,11 @@ public class DockingHelpBroker extends GHelpBroker {
/* /*
// Debug // Debug
Shape box = scaler.createTransformedShape(b); Shape box = scaler.createTransformedShape(b);
g2d.setColor(Color.GREEN); g2d.setColor(Palette.GREEN);
g2d.fill(box); g2d.fill(box);
box = transform.createTransformedShape(box); box = transform.createTransformedShape(box);
g2d.setColor(Color.YELLOW); g2d.setColor(Palette.YELLOW);
g2d.fill(box); g2d.fill(box);
*/ */

View file

@ -1,727 +0,0 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package docking.help;
import java.awt.*;
import java.awt.geom.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.IOException;
import java.net.URL;
import java.util.List;
import javax.help.*;
import javax.help.event.HelpModelEvent;
import javax.help.event.HelpModelListener;
import javax.swing.*;
import javax.swing.text.*;
import javax.swing.text.html.HTML;
import javax.swing.text.html.HTMLDocument;
import org.jdesktop.animation.timing.Animator;
import org.jdesktop.animation.timing.TimingTargetAdapter;
import docking.framework.ApplicationInformationDisplayFactory;
import docking.util.AnimationPainter;
import docking.util.AnimationUtils;
import generic.theme.GColor;
import ghidra.framework.preferences.Preferences;
import ghidra.util.Msg;
import ghidra.util.Swing;
import ghidra.util.bean.GGlassPane;
import resources.ResourceManager;
// NOTE: for JH 2.0, this class has been rewritten to not
// access the 'frame' and 'dialog' variable directly
/**
* Ghidra help broker that displays the help set; sets the Ghidra icon on
* the help frame and attempts to maintain the user window size.
*/
public class GHelpBroker extends DefaultHelpBroker {
private static final List<Image> ICONS = ApplicationInformationDisplayFactory.getWindowIcons();
private static final int MAX_CALLOUT_RETRIES = 3;
private Dimension windowSize = new Dimension(1100, 700);
private boolean initialized;
private JEditorPane htmlEditorPane;
private Animator lastAnimator;
private URL loadingURL;
private PropertyChangeListener pageLoadListener = new PageLoadingListener();
private HelpModelListener helpModelListener = new HelpIDChangedListener();
private Window activationWindow;
// Create the zoom in/out icons that will be added to the default jHelp toolbar.
private static final ImageIcon ZOOM_OUT_ICON =
ResourceManager.loadImage("images/list-remove.png");
private static final ImageIcon ZOOM_IN_ICON = ResourceManager.loadImage("images/list-add.png");
/**
* Construct a new GhidraHelpBroker.
* @param hs java help set associated with this help broker
*/
public GHelpBroker(HelpSet hs) {
super(hs);
}
@Override
// Overridden so that we can call the preferred version of setCurrentURL on the HelpModel,
// which fixes a bug with the history list (SCR 7639)
public void setCurrentURL(final URL URL) {
HelpModel model = getHelpModel();
if (model != null) {
model.setCurrentURL(URL, getHistoryName(URL), null);
}
else {
super.setCurrentURL(URL);
}
}
/* Perform some shenanigans to force Java Help to reload the given URL */
void reloadHelpPage(URL url) {
clearContentViewer();
prepareToCallout(url);
try {
htmlEditorPane.setPage(url);
}
catch (IOException e) {
Msg.error(this, "Unexpected error loading help page: " + url, e);
}
}
private void clearContentViewer() {
htmlEditorPane.getDocument().putProperty(Document.StreamDescriptionProperty, null);
}
private JScrollPane getScrollPane(JEditorPane editorPane) {
Container parent = editorPane.getParent();
while (parent != null) {
if (parent instanceof JScrollPane) {
return (JScrollPane) parent;
}
parent = parent.getParent();
}
return null;
}
private JEditorPane getHTMLEditorPane(JHelpContentViewer contentViewer) {
//
// Intimate Knowledge - construction of the viewer:
//
// -BorderLayout
// -JScrollPane
// -Viewport
// -JHEditorPane extends JEditorPane
//
//
Component[] components = contentViewer.getComponents();
JScrollPane scrollPane = (JScrollPane) components[0];
JViewport viewport = scrollPane.getViewport();
return (JEditorPane) viewport.getView();
}
private HelpModel getHelpModel() {
//
// Unusual Code Alert!: We have opened up access to the help system's HelpModel by way
// of our CustomTOCView object that we install elsewhere. We need
// access to the model because of a bug in the help system
// (SCR 7639). Unfortunately, the Java Help system does not give us
// access to the model directly, but we have opened up the access from
// one of our overriding components. The following code is
// digging-out our custom component to get at the model. An
// alternative approach would be to just use reflection and violate
// security restrictions, but that seemed worse than this solution.
//
WindowPresentation windowPresentation = getWindowPresentation();
HelpSet helpSet = windowPresentation.getHelpSet();
NavigatorView tocView = helpSet.getNavigatorView("TOC");
if (!(tocView instanceof CustomTOCView)) {
// not sure how this could happen
Msg.debug(this, "The help system is not using the CustomTOCView class!");
return null;
}
CustomTOCView customTOCView = (CustomTOCView) tocView;
return customTOCView.getHelpModel();
}
@Override
public void setDisplayed(boolean b) {
if (!b) {
super.setDisplayed(b);
return;
}
// this must be before any call that triggers the help system to create its window
initializeScreenDevice();
WindowPresentation windowPresentation = getWindowPresentation();
updateWindowSize(windowPresentation);
// this has to be before getHelpWindow() or the value returned will be null
super.setDisplayed(b);
initializeUIWindowPresentation(windowPresentation);
}
private void initializeScreenDevice() {
if (initialized) {
return;
}
if (activationWindow == null) {
// This can happen when we show the 'What's New' help page on a fresh install. In
// that case, we were not activated from an existing window, thus, there may
// be no parent window.
return;
}
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice[] gs = ge.getScreenDevices();
GraphicsConfiguration config = activationWindow.getGraphicsConfiguration();
GraphicsDevice parentDevice = config.getDevice();
for (int i = 0; i < gs.length; i++) {
if (gs[i] == parentDevice) {
// update the help window's screen to match that of the parent
setScreen(i);
}
}
}
private void initializeUIWindowPresentation(WindowPresentation windowPresentation) {
Window helpWindow = windowPresentation.getHelpWindow();
Container contentPane = null;
if (helpWindow instanceof JFrame) {
JFrame frame = (JFrame) helpWindow;
installRootPane(frame);
frame.setIconImages(ICONS);
contentPane = frame.getContentPane();
}
else if (helpWindow instanceof JDialog) {
JDialog dialog = (JDialog) helpWindow;
installRootPane(dialog);
contentPane = dialog.getContentPane();
}
initializeUIComponents(contentPane);
}
private void initializeUIComponents(Container contentPane) {
if (initialized) {
return;
}
// the editor pane can be changed out from under us, such as when the UI is updated when
// the theme changes
Component[] components = contentPane.getComponents();
JHelp jHelp = (JHelp) components[0];
JHelpContentViewer contentViewer = jHelp.getContentViewer();
JEditorPane activeHtmlPane = getHTMLEditorPane(contentViewer);
if (activeHtmlPane == htmlEditorPane && initialized) {
return; // already initialized
}
addCustomToolbarItems(jHelp);
htmlEditorPane = getHTMLEditorPane(contentViewer);
// just creating the search wires everything together
HelpModel helpModel = getHelpModel();
helpModel.addHelpModelListener(helpModelListener);
new HelpViewSearcher(jHelp, helpModel);
installActions(jHelp);
initialized = true;
}
void reload() {
clearHighlights();
initialized = false;
if (isDisplayed()) {
setDisplayed(false);
setDisplayed(true);
}
}
private void clearHighlights() {
TextHelpModel helpModel = (TextHelpModel) getHelpModel();
if (helpModel != null) {
helpModel.removeAllHighlights();
}
}
/**
* Create zoom in/out buttons on the default help window toolbar.
* @param jHelp the java help object used to retrieve the help components
*/
protected void addCustomToolbarItems(final JHelp jHelp) {
for (Component component : jHelp.getComponents()) {
if (component instanceof JToolBar) {
JToolBar toolbar = (JToolBar) component;
toolbar.addSeparator();
ImageIcon zoomOutIcon = ResourceManager.getScaledIcon(ZOOM_OUT_ICON, 24, 24);
JButton zoomOutBtn = new JButton(zoomOutIcon);
zoomOutBtn.setToolTipText("Zoom out");
zoomOutBtn.addActionListener(e -> {
GHelpHTMLEditorKit.zoomOut();
// Need to reload the page to force the scroll panes to resize properly. A
// simple revalidate/repaint won't do it.
reloadHelpPage(getCurrentURL());
});
toolbar.add(zoomOutBtn);
ImageIcon zoomInIcon = ResourceManager.getScaledIcon(ZOOM_IN_ICON, 24, 24);
JButton zoomInBtn = new JButton(zoomInIcon);
zoomInBtn.setToolTipText("Zoom in");
zoomInBtn.addActionListener(e -> {
GHelpHTMLEditorKit.zoomIn();
// Need to reload the page to force the scroll panes to resize properly. A
// simple revalidate/repaint won't do it.
reloadHelpPage(getCurrentURL());
});
toolbar.add(zoomInBtn);
// Once we've found the toolbar we can break out of the loop and stop looking for it.
break;
}
}
}
private void installActions(JHelp help) {
JToolBar toolbar = null;
Component[] components = help.getComponents();
for (Component c : components) {
if (c instanceof JToolBar) {
toolbar = (JToolBar) c;
break;
}
}
if (toolbar == null) {
// shouldn't happen
return;
}
// separate the Java help stuff from our actions
toolbar.addSeparator();
ToggleNavigationAid action = new ToggleNavigationAid();
toolbar.add(new JButton(action));
}
private String getHistoryName(URL URL) {
String text = URL.getFile();
int index = text.lastIndexOf('/');
if (index != -1) {
// we want just the filename
text = text.substring(index + 1);
}
String ref = URL.getRef();
if (ref != null) {
text += " - " + ref;
}
return text;
}
private void installRootPane(JFrame frame) {
Component oldGlassPane = frame.getGlassPane();
if (!(oldGlassPane instanceof GGlassPane)) {
GGlassPane gGlassPane = new GGlassPane();
frame.setGlassPane(gGlassPane);
gGlassPane.setVisible(true);
}
}
private void installRootPane(JDialog dialog) {
Component oldGlassPane = dialog.getGlassPane();
if (!(oldGlassPane instanceof GGlassPane)) {
GGlassPane gGlassPane = new GGlassPane();
dialog.setGlassPane(gGlassPane);
gGlassPane.setVisible(true);
}
}
private void updateWindowSize(WindowPresentation presentation) {
if (windowSize == null) {
return;
}
presentation.createHelpWindow();
presentation.setSize(windowSize);
}
@Override
public void setActivationWindow(Window window) {
WindowPresentation windowPresentation = getWindowPresentation();
Window helpWindow = windowPresentation.getHelpWindow();
if (helpWindow == null) {
activationWindow = window;
super.setActivationWindow(window);
return;
}
windowSize = helpWindow.getSize();// remember the previous size
boolean wasModal = isModalWindow(helpWindow);
boolean willBeModal = isModalWindow(window);
if (!wasModal && willBeModal) {
// in this condition, a new window will be shown, but the old one is not properly
// closed by JavaHelp
helpWindow.setVisible(false);
}
super.setActivationWindow(window);
}
private boolean isModalWindow(Window window) {
if (window instanceof Dialog) {
Dialog dialog = (Dialog) window;
if (dialog.isModal()) {
return true;
}
}
return false;
}
private void showNavigationAid() {
String showAidString = Preferences.getProperty(HelpManager.SHOW_AID_KEY);
if (showAidString == null) {
return;
}
boolean showAid = Boolean.parseBoolean(showAidString);
if (!showAid) {
return;
}
calloutReferenceLater();
}
private void calloutReferenceLater() {
SwingUtilities.invokeLater(() -> calloutReference(loadingURL));
}
private void calloutReference(final URL url) {
String ref = url.getRef();
if (ref == null) {
return;
}
final Rectangle area = getReferenceArea(ref);
if (area == null) {
return;
}
doCalloutReference(area, 0);
}
private Rectangle getReferenceArea(String ref) {
HTMLDocument document = (HTMLDocument) htmlEditorPane.getDocument();
HTMLDocument.Iterator iter = document.getIterator(HTML.Tag.A);
for (; iter.isValid(); iter.next()) {
AttributeSet attributes = iter.getAttributes();
String name = (String) attributes.getAttribute(HTML.Attribute.NAME);
if (name == null || !name.equals(ref)) {
continue;
}
try {
int start = iter.getStartOffset();
Rectangle2D startArea = htmlEditorPane.modelToView2D(start);
return startArea.getBounds();
}
catch (BadLocationException ble) {
Msg.trace(this, "Unexpected exception searching for help reference", ble);
}
}
return null;
}
/**
* This method exists to address the threaded timing nature of how the help system loads
* help pages and when the UI is adjusted in response to those changes.
* <p>
* Note: this method will call itself if the view is not yet updated for the requested
* model change. In that case, this method will call itself again later. This may
* need to happen more than once. However, we will only try a few times and
* then just give up.
*
* @param area the area to call out
* @param callCount the number number of times this method has already been called
*/
private void doCalloutReference(final Rectangle area, int callCount) {
if (callCount > MAX_CALLOUT_RETRIES) {
// this probably can't happen, but we don't want to keep calling this method
// forever.
return;
}
WindowPresentation windowPresentation = getWindowPresentation();
Window helpWindow = windowPresentation.getHelpWindow();
Container contentPane = null;
if (helpWindow instanceof JDialog) {
contentPane = ((JDialog) helpWindow).getContentPane();
}
else {
contentPane = ((JFrame) helpWindow).getContentPane();
}
JScrollPane scrollPane = getScrollPane(htmlEditorPane);
JViewport viewport = scrollPane.getViewport();
Point viewPosition = viewport.getViewPosition();
final int numberOfCalls = callCount + 1;
if (viewPosition.x == 0 && viewPosition.y == 0) {
//
// Unusual Code: Not yet rendered! Try again.
//
Swing.runLater(() -> doCalloutReference(area, numberOfCalls));
return;
}
//
// The area of the HTML content is absolute inside of the entire document.
// However, the user is viewing the document inside of a scroll pane. So, we
// want the offset of the element within the viewer, not the absolute position.
//
area.y -= viewPosition.y;
//
// Update the coordinates to be relative to the content pane, which is where we
// are doing the painting.
//
Rectangle relativeArea = SwingUtilities.convertRectangle(scrollPane, area, contentPane);
Shape star = new StarShape(relativeArea.getLocation());
Animator animator =
AnimationUtils.createPaintingAnimator(helpWindow, new LocationHintPainter(star));
if (animator == null) {
return;
}
lastAnimator = animator;
lastAnimator.addTarget(new TimingTargetAdapter() {
@Override
public void end() {
lastAnimator = null;
}
});
}
private void prepareToCallout(URL url) {
if (lastAnimator != null) {
// prevent animations from lingering when moving to new pages
lastAnimator.stop();
}
loadingURL = url;
// TODO
// updateTitle();
if (isCurrentPage(loadingURL)) {
showNavigationAid();
return;// page already loaded; no need to use the listener
}
// listen for the page to be loaded, as it is asynchronous
htmlEditorPane.removePropertyChangeListener("page", pageLoadListener);
htmlEditorPane.addPropertyChangeListener("page", pageLoadListener);
}
private boolean isCurrentPage(URL newURL) {
if (newURL == null) {
return false;// not sure if this can happen
}
String newFile = newURL.getFile();
URL currentURL = htmlEditorPane.getPage();
if (currentURL == null) {
return false;
}
String currentFile = currentURL.getFile();
return newFile.equals(currentFile);
}
//==================================================================================================
// Inner Classes
//==================================================================================================
private class StarShape extends Path2D.Float {
StarShape(Point location) {
this(5, location, 1, .3);// reasonable star qualities
}
StarShape(int points, Point location, double outerRadius, double innerRadius) {
// note: location is the origin of the shape
double angle = Math.PI / points;
GeneralPath path = new GeneralPath();
int scale = 20;
double lr = Math.max(outerRadius, innerRadius);
int width = (int) (scale * (2 * lr));
int height = width;// square bounds
double cx = location.x + width / 2;
double cy = location.y + height / 2;
Point2D.Double center = new Point2D.Double(cx, cy);
// start the first point...
double r = outerRadius;
double x = center.x + Math.cos(0 * angle) * r;
double y = center.y + Math.sin(0 * angle) * r;
path.moveTo(x, y);
// ...the remaining points
for (int i = 1; i < 2 * points; i++) {
r = (i % 2) == 0 ? outerRadius : innerRadius;
x = center.x + Math.cos(i * angle) * r;
y = center.y + Math.sin(i * angle) * r;
path.lineTo(x, y);
}
path.closePath();
// scaled center x/y
double scx = scale * cx;
double scy = scale * cy;
// note: An offset of (width / 2) moves from center to 0. This didn't look quite
// right, so, through trial and error, we updated the offset so that the
// shape's location is just over the beginning of the text that follows the
// anchor, in most cases.
double offsetx = width / 4;
double offsety = height / 4;
// scaled offset x/y
double sox = scx - offsetx;// move the x from center to 0
double soy = scy - offsety;// ...
// delta x/y
double dx = sox - location.x;
double dy = soy - location.y;
// move the origin so that after we scale, it goes back to 0,0
AffineTransform xform = AffineTransform.getTranslateInstance(-dx, -dy);
xform.scale(scale, scale);
Shape shape = xform.createTransformedShape(path);
super.append(shape, true);
}
}
private class PageLoadingListener implements PropertyChangeListener {
@Override
public void propertyChange(PropertyChangeEvent evt) {
showNavigationAid();
htmlEditorPane.removePropertyChangeListener("page", pageLoadListener);
}
}
private class HelpIDChangedListener implements HelpModelListener {
@Override
public void idChanged(HelpModelEvent e) {
prepareToCallout(e.getURL());
}
}
private class LocationHintPainter implements AnimationPainter {
private Color color = new GColor("color.bg.help.hint");
private Shape paintShape;
LocationHintPainter(Shape paintShape) {
this.paintShape = paintShape;
}
@Override
public void paint(GGlassPane glassPane, Graphics graphics, double percentComplete) {
Graphics2D g2d = (Graphics2D) graphics;
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
//
// At 0%, with 100% opacity; at the end, paint with 0% opacity
//
Composite originalComposite = g2d.getComposite();
AlphaComposite alphaComposite = AlphaComposite.getInstance(
AlphaComposite.SrcOver.getRule(), (float) (1 - percentComplete));
g2d.setComposite(alphaComposite);
double transition = 1 - percentComplete;
Color originalColor = g2d.getColor();
AffineTransform originalTransform = g2d.getTransform();
double scale = 4 * transition;
int degrees = (int) (480 * transition);
double rad = Math.toRadians(transition * degrees);
Rectangle b = paintShape.getBounds();
double cx = b.getCenterX();
double cy = b.getCenterY();
double scx = cx * scale;
double scy = cy * scale;
double dcx = scx - cx;
double dcy = scy - cy;
AffineTransform scaler = new AffineTransform();
scaler.translate(-dcx, -dcy);
scaler.scale(scale, scale);
Shape scaled = scaler.createTransformedShape(paintShape);
AffineTransform rotater = new AffineTransform();
rotater.rotate(rad, cx, cy);
Shape finalShape = rotater.createTransformedShape(scaled);
/*
// Debug
Shape box = scaler.createTransformedShape(b);
g2d.setColor(Palette.GREEN);
g2d.fill(box);
box = transform.createTransformedShape(box);
g2d.setColor(Palette.YELLOW);
g2d.fill(box);
*/
g2d.setColor(color);
g2d.fill(finalShape);
g2d.setColor(originalColor);
g2d.setTransform(originalTransform);
g2d.setComposite(originalComposite);
}
}
}

View file

@ -1,588 +0,0 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package docking.help;
import java.awt.Desktop;
import java.awt.Image;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.*;
import java.net.*;
import java.util.*;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.JEditorPane;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;
import javax.swing.text.*;
import javax.swing.text.html.*;
import javax.swing.text.html.HTML.Tag;
import generic.jar.ResourceFile;
import generic.theme.GColor;
import ghidra.framework.Application;
import ghidra.framework.preferences.Preferences;
import ghidra.util.Msg;
import resources.*;
import utilities.util.FileUtilities;
/**
* A class that allows Ghidra to intercept JavaHelp navigation events in order to resolve them to
* Ghidra's help system. Without this class, contribution plugins have no way of referencing help
* documents within Ghidra's default help location.
* <p>
* This class is currently installed by the {@link GHelpSet}.
*
* @see GHelpSet
*/
public class GHelpHTMLEditorKit extends HTMLEditorKit {
private static final String G_HELP_STYLE_SHEET = "help/shared/Frontpage.css";
private static final Pattern EXTERNAL_URL_PATTERN = Pattern.compile("https?://.*");
/** A pattern to strip the font size value from a line of CSS */
private static final Pattern FONT_SIZE_PATTERN = Pattern.compile("font-size:\\s*(\\d{1,2})");
private static final String HELP_WINDOW_ZOOM_FACTOR = "HELP.WINDOW.FONT.SIZE.MODIFIER";
private static int fontSizeModifier;
/**
* A mapping of known style sheet colors to convert from the values in the style sheet to colors
* defined in the system theme.
*/
private static final Map<String, GColor> colorsById = new HashMap<>();
static {
colorsById.put("h1", new GColor("color.fg.help.selector.h1"));
colorsById.put("h2", new GColor("color.fg.help.selector.h2"));
colorsById.put("h3", new GColor("color.fg.help.selector.h3"));
colorsById.put("p.providedbyplugin",
new GColor("color.fg.help.selector.p.provided.by.plugin"));
colorsById.put("p.relatedtopic",
new GColor("color.fg.help.selector.p.related.topic"));
colorsById.put("th", new GColor("color.fg.help.selector.th"));
colorsById.put("code", new GColor("color.fg.help.selector.code"));
colorsById.put("code.path", new GColor("color.fg.help.selector.code.path"));
}
private static final Pattern COLOR_PATTERN = Pattern.compile("(color:\\s*#{0,1}\\w+;)");
private HyperlinkListener[] delegateListeners = null;
private HyperlinkListener resolverHyperlinkListener;
public GHelpHTMLEditorKit() {
fontSizeModifier =
Integer.valueOf(Preferences.getProperty(HELP_WINDOW_ZOOM_FACTOR, "0", true));
}
@Override
public ViewFactory getViewFactory() {
return new GHelpHTMLFactory();
}
@Override
public void install(JEditorPane c) {
super.install(c);
delegateListeners = c.getHyperlinkListeners();
for (HyperlinkListener listener : delegateListeners) {
c.removeHyperlinkListener(listener);
}
resolverHyperlinkListener = new ResolverHyperlinkListener();
c.addHyperlinkListener(resolverHyperlinkListener);
// add a listener to report trace information
c.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
String propertyName = evt.getPropertyName();
if ("page".equals(propertyName)) {
Msg.trace(this, "Page loaded: " + evt.getNewValue());
}
}
});
}
@Override
public void deinstall(JEditorPane c) {
c.removeHyperlinkListener(resolverHyperlinkListener);
for (HyperlinkListener listener : delegateListeners) {
c.addHyperlinkListener(listener);
}
super.deinstall(c);
}
private class ResolverHyperlinkListener implements HyperlinkListener {
@Override
public void hyperlinkUpdate(HyperlinkEvent e) {
if (delegateListeners == null) {
return;
}
if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
if (isExternalLink(e)) {
browseExternalLink(e);
return;
}
Msg.trace(this, "Link activated: " + e.getURL());
e = validateURL(e);
Msg.trace(this, "Validated event: " + e.getURL());
}
for (HyperlinkListener listener : delegateListeners) {
listener.hyperlinkUpdate(e);
}
}
}
private boolean isExternalLink(HyperlinkEvent e) {
String description = e.getDescription();
return description != null && EXTERNAL_URL_PATTERN.matcher(description).matches();
}
private void browseExternalLink(HyperlinkEvent e) {
String description = e.getDescription();
if (!Desktop.isDesktopSupported()) {
Msg.info(this, "Unable to launch external browser for " + description);
return;
}
try {
// use an external browser
URI uri = e.getURL().toURI();
Desktop.getDesktop().browse(uri);
}
catch (URISyntaxException | IOException e1) {
Msg.error(this, "Error browsing to external URL " + description, e1);
}
}
/**
* Tests the URL of the given event. If the URL is invalid, a new event may be created if
* a new, valid URL can be created. Creates a new event with a patched URL if
* the given event's URL is invalid.
*/
private HyperlinkEvent validateURL(HyperlinkEvent event) {
URL url = event.getURL();
try {
url.openStream();// assume that this will fail if the file does not exist
}
catch (IOException ioe) {
// assume this means that the url is invalid
Msg.trace(this, "URL of link is invalid: " + url.toExternalForm());
return maybeCreateNewHyperlinkEventWithUpdatedURL(event);
}
return event;// url is fine
}
/** Generates a new event with a URL based upon Ghidra's resources if needed. */
private HyperlinkEvent maybeCreateNewHyperlinkEventWithUpdatedURL(HyperlinkEvent event) {
Element element = event.getSourceElement();
if (element == null) {
return event;// this shouldn't happen since we were triggered from an A tag
}
AttributeSet a = element.getAttributes();
AttributeSet anchor = (AttributeSet) a.getAttribute(HTML.Tag.A);
if (anchor == null) {
return event;// this shouldn't happen since we were triggered from an A tag
}
String HREF = (String) anchor.getAttribute(HTML.Attribute.HREF);
Msg.trace(this, "HREF of <a> tag: " + HREF);
URL newUrl = getURLForHREFFromResources(HREF);
if (newUrl == null) {
return event;// unable to locate a resource by the name--bad link!
}
return new HyperlinkEvent(event.getSource(), event.getEventType(), newUrl,
event.getDescription(), event.getSourceElement());
}
private URL getURLForHREFFromResources(String originalHREF) {
int anchorIndex = originalHREF.indexOf("#");
String HREF = originalHREF;
String anchor = null;
if (anchorIndex != -1) {
HREF = HREF.substring(0, anchorIndex);
anchor = originalHREF.substring(anchorIndex);
}
// look for a URL using an installation environment setup...
URL newUrl = ResourceManager.getResource(HREF);
if (newUrl != null) {
return createURLWithAnchor(newUrl, anchor);
}
//
// The item was not found by the ResourceManager (i.e., it is not in a 'resources'
// directory). See if it may be a relative link to a build's installation root (like
// a file in <install dir>/docs).
//
newUrl = findApplicationfile(HREF);
return newUrl;
}
private URL createURLWithAnchor(URL anchorlessURL, String anchor) {
if (anchorlessURL == null) {
return anchorlessURL;
}
if (anchor == null) {
// nothing to do
return anchorlessURL;
}
try {
// put the anchor back into the URL
return new URL(anchorlessURL, anchor);
}
catch (MalformedURLException e) {
// shouldn't happen, since the file exists
Msg.showError(this, null, "Unexpected Error",
"Unexpected error creating a valid URL: " + anchorlessURL + "#" + anchor);
return null;
}
}
@Override
public void read(Reader in, Document doc, int pos) throws IOException, BadLocationException {
super.read(in, doc, pos);
HTMLDocument htmlDoc = (HTMLDocument) doc;
loadGHelpStyleSheet(htmlDoc);
}
private void loadGHelpStyleSheet(HTMLDocument doc) {
Reader reader = getGStyleSheetReader();
if (reader == null) {
return;
}
StyleSheet ss = doc.getStyleSheet();
try {
ss.loadRules(reader, null);
}
catch (IOException e) {
// shouldn't happen
Msg.debug(this, "Unable to load help style sheet");
}
}
private Reader getGStyleSheetReader() {
URL url = getGStyleSheetURL();
if (url == null) {
return null;
}
StringBuilder buffy = new StringBuilder();
try {
List<String> lines = FileUtilities.getLines(url);
for (String line : lines) {
StringBuilder lineBuilder = new StringBuilder();
changePixels(line, fontSizeModifier, lineBuilder);
String updatedLine = lineBuilder.toString();
lineBuilder.delete(0, lineBuilder.length());
changeColor(updatedLine, lineBuilder);
buffy.append(lineBuilder.toString());
buffy.append('\n');
}
}
catch (IOException e) {
// shouldn't happen
Msg.debug(this, "Unable to read the lines of the help style sheet: " + url);
}
StringReader reader = new StringReader(buffy.toString());
return reader;
}
private void changeColor(String line, StringBuilder buffy) {
int blockStart = line.indexOf("{");
if (blockStart == -1) {
buffy.append(line);
return;
}
String cssSelector = line.substring(0, blockStart).trim();
cssSelector = cssSelector.toLowerCase(); // normalize
GColor gColor = colorsById.get(cssSelector);
if (gColor == null) {
buffy.append(line);
return;
}
Matcher matcher = COLOR_PATTERN.matcher(line);
if (matcher.find()) {
matcher.appendReplacement(buffy, "color: " + gColor.toHexString() + ";");
}
matcher.appendTail(buffy);
}
private void changePixels(String line, int amount, StringBuilder buffy) {
Matcher matcher = FONT_SIZE_PATTERN.matcher(line);
while (matcher.find()) {
String oldFontSize = matcher.group(1);
String adjustFontSize = adjustFontSize(oldFontSize);
matcher.appendReplacement(buffy, "font-size: " + adjustFontSize);
}
matcher.appendTail(buffy);
}
private String adjustFontSize(String sizeString) {
try {
int size = Integer.parseInt(sizeString);
String adjusted = Integer.toString(size + fontSizeModifier);
return adjusted;
}
catch (NumberFormatException e) {
Msg.debug(this, "Unable to parse font size string '" + sizeString + "'");
}
return sizeString;
}
private URL getGStyleSheetURL() {
URL GStyleSheetURL = ResourceManager.getResource(G_HELP_STYLE_SHEET);
if (GStyleSheetURL != null) {
return GStyleSheetURL;
}
return findModuleFile("help/shared/FrontPage.css");
}
private URL findApplicationfile(String relativePath) {
ResourceFile installDir = Application.getInstallationDirectory();
ResourceFile file = new ResourceFile(installDir, relativePath);
if (file.exists()) {
try {
return file.toURL();
}
catch (MalformedURLException e) {
Msg.showError(this, null, "Unexpected Error",
"Unexpected error parsing file to URL: " + file);
}
}
return null;
}
private URL findModuleFile(String relativePath) {
Collection<ResourceFile> moduleDirs = Application.getModuleRootDirectories();
for (ResourceFile dir : moduleDirs) {
ResourceFile file = new ResourceFile(dir, relativePath);
if (file.exists()) {
try {
return file.toURL();
}
catch (MalformedURLException e) {
Msg.showError(this, null, "Unexpected Error",
"Unexpected error parsing file to URL: " + file);
return null;
}
}
}
return null;
}
public static void zoomOut() {
fontSizeModifier -= 2;
saveZoomFactor();
}
public static void zoomIn() {
fontSizeModifier += 2;
saveZoomFactor();
}
private static void saveZoomFactor() {
Preferences.setProperty(HELP_WINDOW_ZOOM_FACTOR, Integer.toString(fontSizeModifier));
Preferences.store();
}
//==================================================================================================
// Inner Classes
//==================================================================================================
private class GHelpHTMLFactory extends HTMLFactory {
@Override
public View create(Element e) {
AttributeSet attributes = e.getAttributes();
Object elementName = attributes.getAttribute(AbstractDocument.ElementNameAttribute);
if (elementName != null) {
// not an HTML element
return super.create(e);
}
Object html = attributes.getAttribute(StyleConstants.NameAttribute);
if (html instanceof HTML.Tag) {
HTML.Tag tag = (Tag) html;
if (tag == HTML.Tag.IMG) {
return new GHelpImageView(e);
}
}
return super.create(e);
}
}
/**
* Overridden to allow us to find images that are defined as constants in places like
* {@link Icons}
*/
private class GHelpImageView extends ImageView {
/*
* Unusual Code Alert!
* This class exists to enable our help system to find custom icons defined in source
* code. The default behavior herein is to supply a URL to the base class to load. This
* works fine.
*
* There is another use case where we wish to have the base class load an image of our
* choosing. Why? Well, we modify, in memory, some icons we use. We do this for things
* like overlays and rotations.
*
* In order to have our base class use the image that we want (and not the one
* it loads via a URL), we have to play a small game. We have to allow the base class
* to load the image it wants, which is done asynchronously. If we install our custom
* image during that process, the loading will throw away the image and not render
* anything.
*
* To get the base class to use our image, we override getImage(). However, we should
* only return our image when the base class is finished loading. (See the base class'
* paint() method for why we need to do this.)
*
* Note: if we start seeing unusual behavior, like images not rendering, or any size
* issues, then we can revert this code.
*/
private Image image;
private float spanX;
private float spanY;
public GHelpImageView(Element elem) {
super(elem);
}
@Override
public Image getImage() {
Image superImage = super.getImage();
if (image == null) {
// no custom image
return superImage;
}
if (isLoading()) {
return superImage;
}
return image;
}
private boolean isLoading() {
return spanX < 1 || spanY < 1;
}
@Override
public float getPreferredSpan(int axis) {
float span = super.getPreferredSpan(axis);
if (axis == View.X_AXIS) {
spanX = span;
}
else {
spanY = span;
}
return span;
}
@Override
public URL getImageURL() {
AttributeSet attributes = getElement().getAttributes();
Object src = attributes.getAttribute(HTML.Attribute.SRC);
if (src == null) {
return null;
}
String srcString = src.toString();
if (isJavaCode(srcString)) {
return installImageFromJavaCode(srcString);
}
URL url = doGetImageURL(srcString);
return url;
}
private URL installImageFromJavaCode(String srcString) {
IconProvider iconProvider = getIconFromJavaCode(srcString);
if (iconProvider == null || iconProvider.isInvalid()) {
return null;
}
this.image = iconProvider.getImage();
URL url = iconProvider.getOrCreateUrl();
return url;
}
private URL doGetImageURL(String srcString) {
HTMLDocument htmlDocument = (HTMLDocument) getDocument();
URL context = htmlDocument.getBase();
try {
URL url = new URL(context, srcString);
if (FileUtilities.exists(url.toURI())) {
// it's a good one, let it through
return url;
}
}
catch (MalformedURLException | URISyntaxException e) {
// check below
}
// Try the ResourceManager. This will work for images that start with GHelp
// relative link syntax such as 'help/', 'help/topics/' and 'images/'
URL resource = ResourceManager.getResource(srcString);
return resource;
}
private boolean isJavaCode(String src) {
// not sure of the best way to handle this--be exact for now
return Icons.isIconsReference(src);
}
private IconProvider getIconFromJavaCode(String src) {
return Icons.getIconForIconsReference(src);
}
}
}

View file

@ -1,121 +0,0 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package docking.help;
import java.awt.Component;
import java.net.URL;
import ghidra.util.HelpLocation;
/**
* <code>HelpService</code> defines a service for displaying Help content by an ID or URL.
*/
public interface HelpService {
public static final String DUMMY_HELP_SET_NAME = "Dummy_HelpSet.hs";
/**
* Display the Help content identified by the help object.
*
* @param helpObject the object to which help was previously registered
* @param infoOnly display {@link HelpLocation} information only, not the help UI
* @param parent requesting component
*
* @see #registerHelp(Object, HelpLocation)
*/
public void showHelp(Object helpObject, boolean infoOnly, Component parent);
/**
* Display the help page for the given URL. This is a specialty method for displaying
* help when a specific file is desired, like an introduction page. Showing help for
* objects within the system is accomplished by calling
* {@link #showHelp(Object, boolean, Component)}.
*
* @param url the URL to display
* @see #showHelp(Object, boolean, Component)
*/
public void showHelp(URL url);
/**
* Display the help page for the given help location.
*
* @param location the location to display.
* @see #showHelp(Object, boolean, Component)
*/
public void showHelp(HelpLocation location);
/**
* Signals to the help system to ignore the given object when searching for and validating
* help. Once this method has been called, no help can be registered for the given object.
*
* @param helpObject the object to exclude from the help system.
*/
public void excludeFromHelp(Object helpObject);
/**
* Returns true if the given object is meant to be ignored by the help system
*
* @param helpObject the object to check
* @return true if ignored
* @see #excludeFromHelp(Object)
*/
public boolean isExcludedFromHelp(Object helpObject);
/**
* Register help for a specific object.
*
* <P>Do not call this method will a <code>null</code> help location. Instead, to signal that
* an item has no help, call {@link #excludeFromHelp(Object)}.
*
* @param helpObject the object to associate the specified help location with
* @param helpLocation help content location
*/
public void registerHelp(Object helpObject, HelpLocation helpLocation);
/**
* Removes this object from the help system. This method is useful, for example,
* when a single Java {@link Component} will have different help locations
* assigned over its lifecycle.
*
* @param helpObject the object for which to clear help
*/
public void clearHelp(Object helpObject);
/**
* Returns the registered (via {@link #registerHelp(Object, HelpLocation)} help
* location for the given object; null if there is no registered
* help.
*
* @param object The object for which to find a registered HelpLocation.
* @return the registered HelpLocation
* @see #registerHelp(Object, HelpLocation)
*/
public HelpLocation getHelpLocation(Object object);
/**
* Returns true if the help system has been initialized properly; false if help does not
* exist or is not working.
*
* @return true if the help system has found the applications help content and has finished
* initializing
*/
public boolean helpExists();
/**
* Called when a major system even happens, such as changing the system theme.
*/
public void reload();
}

View file

@ -0,0 +1,121 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package docking.theme.gui;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.*;
import java.beans.PropertyChangeEvent;
import javax.swing.JPanel;
import javax.swing.ListSelectionModel;
import javax.swing.table.TableColumn;
import docking.ActionContext;
import docking.action.ActionContextProvider;
import docking.widgets.table.GFilterTable;
import docking.widgets.table.GTable;
import generic.theme.ColorValue;
import generic.theme.Gui;
import ghidra.util.Swing;
/**
* Color Table for Theme Dialog
*/
public class ThemeColorTable extends JPanel implements ActionContextProvider {
private ThemeColorTableModel colorTableModel;
private ColorValueEditor colorEditor = new ColorValueEditor(this::colorValueChanged);
private GTable table;
private GFilterTable<ColorValue> filterTable;
public ThemeColorTable() {
super(new BorderLayout());
colorTableModel = new ThemeColorTableModel();
filterTable = new GFilterTable<>(colorTableModel);
table = filterTable.getTable();
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
table.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
ColorValue colorValue = filterTable.getSelectedRowObject();
if (colorValue != null) {
colorEditor.editValue(colorValue);
}
e.consume();
}
}
});
table.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) {
ColorValue value = filterTable.getItemAt(e.getPoint());
int col = filterTable.getColumn(e.getPoint());
TableColumn column = table.getColumnModel().getColumn(col);
Object identifier = column.getIdentifier();
if ("Current Color".equals(identifier) || "Id".equals(identifier)) {
colorEditor.editValue(value);
}
}
}
});
add(filterTable, BorderLayout.CENTER);
}
void colorValueChanged(PropertyChangeEvent event) {
// run later - don't rock the boat in the middle of a listener callback
Swing.runLater(() -> {
ColorValue newValue = (ColorValue) event.getNewValue();
Gui.setColor(newValue);
});
}
/**
* Returns the current values displayed in the table
*/
public void reloadCurrent() {
colorTableModel.reloadCurrent();
}
/**
* Reloads all the values displayed in the table
*/
public void reloadAll() {
colorTableModel.reloadAll();
}
@Override
public ActionContext getActionContext(MouseEvent e) {
if (e.getSource() == table) {
ColorValue currentValue = filterTable.getSelectedRowObject();
if (currentValue == null) {
return null;
}
String id = currentValue.getId();
ColorValue themeValue = colorTableModel.getThemeValue(id);
return new ThemeTableContext<Color>(currentValue, themeValue);
}
return null;
}
}

View file

@ -67,6 +67,13 @@ public class ThemeColorTableModel extends GDynamicColumnTableModel<ColorValue, O
fireTableDataChanged(); fireTableDataChanged();
} }
/**
* Returns the original value for the current theme
*/
public ColorValue getThemeValue(String id) {
return themeValues.getColor(id);
}
private void load() { private void load() {
currentValues = Gui.getAllValues(); currentValues = Gui.getAllValues();
colors = currentValues.getColors(); colors = currentValues.getColors();
@ -203,11 +210,11 @@ public class ThemeColorTableModel extends GDynamicColumnTableModel<ColorValue, O
return "<No Value>"; return "<No Value>";
} }
Color color = resolvedColor.color(); Color color = resolvedColor.color();
String text = WebColors.toString(color, false);
String name = WebColors.toWebColorName(color); String name = WebColors.toWebColorName(color);
if (resolvedColor.refId() != null) { if (resolvedColor.refId() != null) {
return "[" + resolvedColor.refId() + "] " + text; return "[" + resolvedColor.refId() + "] ";
} }
String text = WebColors.toString(color, false);
if (name != null) { if (name != null) {
text += " (" + name + ")"; text += " (" + name + ")";
} }
@ -250,4 +257,5 @@ public class ThemeColorTableModel extends GDynamicColumnTableModel<ColorValue, O
} }
record ResolvedColor(String id, String refId, Color color) {/**/} record ResolvedColor(String id, String refId, Color color) {/**/}
} }

View file

@ -18,43 +18,36 @@ package docking.theme.gui;
import java.awt.BorderLayout; import java.awt.BorderLayout;
import java.awt.Component; import java.awt.Component;
import java.awt.event.*; import java.awt.event.*;
import java.beans.PropertyChangeEvent;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.swing.*; import javax.swing.*;
import javax.swing.table.TableColumn;
import docking.DialogComponentProvider; import docking.*;
import docking.DockingWindowManager; import docking.action.ActionContextProvider;
import docking.action.DockingAction; import docking.action.DockingAction;
import docking.action.builder.ActionBuilder; import docking.action.builder.ActionBuilder;
import docking.widgets.OptionDialog; import docking.widgets.OptionDialog;
import docking.widgets.combobox.GhidraComboBox; import docking.widgets.combobox.GhidraComboBox;
import docking.widgets.table.GFilterTable;
import docking.widgets.table.GTable;
import generic.theme.*; import generic.theme.*;
import ghidra.util.MessageType; import ghidra.util.*;
import ghidra.util.Swing;
/** /**
* Primary dialog for editing Themes. * Primary dialog for editing Themes.
*/ */
public class ThemeDialog extends DialogComponentProvider { public class ThemeDialog extends DialogComponentProvider {
private static ThemeDialog INSTANCE; private static ThemeDialog INSTANCE;
private ThemeColorTableModel colorTableModel;
private ThemeFontTableModel fontTableModel;
private ThemeIconTableModel iconTableModel;
private ColorValueEditor colorEditor = new ColorValueEditor(this::colorValueChanged);
private FontValueEditor fontEditor = new FontValueEditor(this::fontValueChanged);
private IconValueEditor iconEditor = new IconValueEditor(this::iconValueChanged);
private JButton saveButton; private JButton saveButton;
private JButton restoreButton; private JButton restoreButton;
private GhidraComboBox<String> combo; private GhidraComboBox<String> combo;
private ItemListener comboListener = this::themeComboChanged; private ItemListener comboListener = this::themeComboChanged;
private ThemeListener listener = new DialogThemeListener(); private ThemeListener listener = new DialogThemeListener();
private JTabbedPane tabbedPane;
private ThemeColorTable colorTable;
private ThemeFontTable fontTable;
private ThemeIconTable iconTable;
public ThemeDialog() { public ThemeDialog() {
super("Theme Dialog", false); super("Theme Dialog", false);
@ -69,14 +62,26 @@ public class ThemeDialog extends DialogComponentProvider {
updateButtons(); updateButtons();
createActions(); createActions();
Gui.addThemeListener(listener); Gui.addThemeListener(listener);
setHelpLocation(new HelpLocation("Theming", "Edit_Theme"));
} }
private void createActions() { private void createActions() {
DockingAction reloadDefaultsAction = new ActionBuilder("Reload Ghidra Defaults", getTitle()) DockingAction reloadDefaultsAction = new ActionBuilder("Reload Ghidra Defaults", getTitle())
.toolBarIcon(new GIcon("icon.refresh")) .toolBarIcon(new GIcon("icon.refresh"))
.helpLocation(new HelpLocation("Theming", "Reload_Ghidra_Defaults"))
.onAction(e -> reloadDefaultsCallback()) .onAction(e -> reloadDefaultsCallback())
.build(); .build();
addAction(reloadDefaultsAction); addAction(reloadDefaultsAction);
DockingAction resetValueAction = new ActionBuilder("Restore Value", getTitle())
.popupMenuPath("Restore Value")
.withContext(ThemeTableContext.class)
.enabledWhen(c -> c.isChanged())
.popupWhen(c -> true)
.helpLocation(new HelpLocation("Theming", "Restore_Value"))
.onAction(c -> c.getThemeValue().makeCurrentValue())
.build();
addAction(resetValueAction);
} }
@Override @Override
@ -129,9 +134,9 @@ public class ThemeDialog extends DialogComponentProvider {
} }
private void reset() { private void reset() {
colorTableModel.reloadAll(); colorTable.reloadAll();
fontTableModel.reloadAll(); fontTable.reloadAll();
iconTableModel.reloadAll(); iconTable.reloadAll();
updateButtons(); updateButtons();
updateCombo(); updateCombo();
} }
@ -159,45 +164,9 @@ public class ThemeDialog extends DialogComponentProvider {
else { else {
setStatusText(""); setStatusText("");
} }
colorTableModel.reloadAll(); colorTable.reloadAll();
fontTableModel.reloadAll(); fontTable.reloadAll();
iconTableModel.reloadAll(); iconTable.reloadAll();
});
}
protected void editColor(ColorValue value) {
colorEditor.editValue(value);
}
protected void editFont(FontValue value) {
fontEditor.editValue(value);
}
protected void editIcon(IconValue value) {
iconEditor.editValue(value);
}
void colorValueChanged(PropertyChangeEvent event) {
// run later - don't rock the boat in the middle of a listener callback
Swing.runLater(() -> {
ColorValue newValue = (ColorValue) event.getNewValue();
Gui.setColor(newValue);
});
}
void fontValueChanged(PropertyChangeEvent event) {
// run later - don't rock the boat in the middle of a listener callback
Swing.runLater(() -> {
FontValue newValue = (FontValue) event.getNewValue();
Gui.setFont(newValue);
});
}
void iconValueChanged(PropertyChangeEvent event) {
// run later - don't rock the boat in the middle of a listener callback
Swing.runLater(() -> {
IconValue newValue = (IconValue) event.getNewValue();
Gui.setIcon(newValue);
}); });
} }
@ -253,130 +222,16 @@ public class ThemeDialog extends DialogComponentProvider {
} }
private Component buildTabedTables() { private Component buildTabedTables() {
JTabbedPane tabbedPane = new JTabbedPane(); tabbedPane = new JTabbedPane();
tabbedPane.add("Colors", buildColorTable()); colorTable = new ThemeColorTable();
tabbedPane.add("Fonts", buildFontTable()); fontTable = new ThemeFontTable();
tabbedPane.add("Icons", buildIconTable()); iconTable = new ThemeIconTable();
tabbedPane.add("Colors", colorTable);
tabbedPane.add("Fonts", fontTable);
tabbedPane.add("Icons", iconTable);
return tabbedPane; return tabbedPane;
} }
private JComponent buildFontTable() {
fontTableModel = new ThemeFontTableModel();
GFilterTable<FontValue> filterTable = new GFilterTable<>(fontTableModel);
GTable table = filterTable.getTable();
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
table.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
FontValue fontValue = filterTable.getSelectedRowObject();
if (fontValue != null) {
editFont(fontValue);
}
e.consume();
}
}
});
table.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) {
FontValue value = filterTable.getItemAt(e.getPoint());
int col = filterTable.getColumn(e.getPoint());
TableColumn column = table.getColumnModel().getColumn(col);
Object identifier = column.getIdentifier();
if ("Current Font".equals(identifier) || "Id".equals(identifier)) {
editFont(value);
}
}
}
});
return filterTable;
}
private JComponent buildIconTable() {
iconTableModel = new ThemeIconTableModel();
GFilterTable<IconValue> filterTable = new GFilterTable<>(iconTableModel);
GTable table = filterTable.getTable();
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
table.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
IconValue iconValue = filterTable.getSelectedRowObject();
if (iconValue != null) {
editIcon(iconValue);
}
e.consume();
}
}
});
table.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) {
IconValue value = filterTable.getItemAt(e.getPoint());
int col = filterTable.getColumn(e.getPoint());
TableColumn column = table.getColumnModel().getColumn(col);
Object identifier = column.getIdentifier();
if ("Current Icon".equals(identifier) || "Id".equals(identifier)) {
editIcon(value);
}
}
}
});
return filterTable;
}
private JComponent buildColorTable() {
colorTableModel = new ThemeColorTableModel();
GFilterTable<ColorValue> filterTable = new GFilterTable<>(colorTableModel);
GTable table = filterTable.getTable();
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
table.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
ColorValue colorValue = filterTable.getSelectedRowObject();
if (colorValue != null) {
editColor(colorValue);
}
e.consume();
}
}
});
table.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) {
ColorValue value = filterTable.getItemAt(e.getPoint());
int col = filterTable.getColumn(e.getPoint());
TableColumn column = table.getColumnModel().getColumn(col);
Object identifier = column.getIdentifier();
if ("Current Color".equals(identifier) || "Id".equals(identifier)) {
editColor(value);
}
}
}
});
return filterTable;
}
private JButton createRestoreButton() { private JButton createRestoreButton() {
restoreButton = new JButton("Restore"); restoreButton = new JButton("Restore");
restoreButton.setMnemonic('R'); restoreButton.setMnemonic('R');
@ -411,6 +266,13 @@ public class ThemeDialog extends DialogComponentProvider {
super.close(); super.close();
} }
@Override
public ActionContext getActionContext(MouseEvent event) {
ActionContextProvider contextProvider =
(ActionContextProvider) tabbedPane.getSelectedComponent();
return contextProvider.getActionContext(event);
}
class DialogThemeListener implements ThemeListener { class DialogThemeListener implements ThemeListener {
@Override @Override
public void themeChanged(ThemeEvent event) { public void themeChanged(ThemeEvent event) {
@ -419,13 +281,13 @@ public class ThemeDialog extends DialogComponentProvider {
return; return;
} }
if (event.hasAnyColorChanged()) { if (event.hasAnyColorChanged()) {
colorTableModel.reloadCurrent(); colorTable.reloadCurrent();
} }
if (event.hasAnyFontChanged()) { if (event.hasAnyFontChanged()) {
fontTableModel.reloadCurrent(); fontTable.reloadCurrent();
} }
if (event.hasAnyIconChanged()) { if (event.hasAnyIconChanged()) {
iconTableModel.reloadCurrent(); iconTable.reloadCurrent();
} }
updateButtons(); updateButtons();
} }

View file

@ -0,0 +1,120 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package docking.theme.gui;
import java.awt.BorderLayout;
import java.awt.Font;
import java.awt.event.*;
import java.beans.PropertyChangeEvent;
import javax.swing.JPanel;
import javax.swing.ListSelectionModel;
import javax.swing.table.TableColumn;
import docking.ActionContext;
import docking.action.ActionContextProvider;
import docking.widgets.table.GFilterTable;
import docking.widgets.table.GTable;
import generic.theme.FontValue;
import generic.theme.Gui;
import ghidra.util.Swing;
/**
* Font Table for Theme Dialog
*/
public class ThemeFontTable extends JPanel implements ActionContextProvider {
private ThemeFontTableModel fontTableModel;
private FontValueEditor fontEditor = new FontValueEditor(this::fontValueChanged);
private GTable table;
private GFilterTable<FontValue> filterTable;
public ThemeFontTable() {
super(new BorderLayout());
fontTableModel = new ThemeFontTableModel();
filterTable = new GFilterTable<>(fontTableModel);
table = filterTable.getTable();
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
table.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
FontValue fontValue = filterTable.getSelectedRowObject();
if (fontValue != null) {
fontEditor.editValue(fontValue);
}
e.consume();
}
}
});
table.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) {
FontValue value = filterTable.getItemAt(e.getPoint());
int col = filterTable.getColumn(e.getPoint());
TableColumn column = table.getColumnModel().getColumn(col);
Object identifier = column.getIdentifier();
if ("Current Font".equals(identifier) || "Id".equals(identifier)) {
fontEditor.editValue(value);
}
}
}
});
add(filterTable, BorderLayout.CENTER);
}
void fontValueChanged(PropertyChangeEvent event) {
// run later - don't rock the boat in the middle of a listener callback
Swing.runLater(() -> {
FontValue newValue = (FontValue) event.getNewValue();
Gui.setFont(newValue);
});
}
/**
* Reloads all the values displayed in the table
*/
public void reloadAll() {
fontTableModel.reloadAll();
}
/**
* Returns the current values displayed in the table
*/
public void reloadCurrent() {
fontTableModel.reloadCurrent();
}
@Override
public ActionContext getActionContext(MouseEvent e) {
if (e.getSource() == table) {
FontValue currentValue = filterTable.getSelectedRowObject();
if (currentValue == null) {
return null;
}
String id = currentValue.getId();
FontValue themeValue = fontTableModel.getThemeValue(id);
return new ThemeTableContext<Font>(currentValue, themeValue);
}
return null;
}
}

View file

@ -100,7 +100,7 @@ public class ThemeFontTableModel extends GDynamicColumnTableModel<FontValue, Obj
return "<No Value>"; return "<No Value>";
} }
if (fontValue.getReferenceId() != null) { if (fontValue.getReferenceId() != null) {
return fontValue.getReferenceId(); return "[" + fontValue.getReferenceId() + "]";
} }
Font font = fontValue.getRawValue(); Font font = fontValue.getRawValue();
@ -178,8 +178,6 @@ public class ThemeFontTableModel extends GDynamicColumnTableModel<FontValue, Obj
} }
private class ThemeFontRenderer extends AbstractGColumnRenderer<FontValue> { private class ThemeFontRenderer extends AbstractGColumnRenderer<FontValue> {
private Font regularFont = new Font("Monospaced", Font.BOLD, 12);
private Font indirectFont = new Font("Monospaced", Font.ITALIC, 12);
@Override @Override
public Component getTableCellRendererComponent(GTableCellRenderingData data) { public Component getTableCellRendererComponent(GTableCellRenderingData data) {
@ -187,12 +185,6 @@ public class ThemeFontTableModel extends GDynamicColumnTableModel<FontValue, Obj
FontValue fontValue = (FontValue) data.getValue(); FontValue fontValue = (FontValue) data.getValue();
String text = getValueText(fontValue); String text = getValueText(fontValue);
if (fontValue.getReferenceId() != null) {
label.setFont(indirectFont);
}
else {
label.setFont(regularFont);
}
label.setText(text); label.setText(text);
label.setOpaque(true); label.setOpaque(true);
return label; return label;
@ -205,4 +197,13 @@ public class ThemeFontTableModel extends GDynamicColumnTableModel<FontValue, Obj
} }
/**
* Returns the original value for the id as defined by the current theme
* @param id the resource id to get a font value for
* @return the original value for the id as defined by the current theme
*/
public FontValue getThemeValue(String id) {
return themeValues.getFont(id);
}
} }

View file

@ -0,0 +1,116 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package docking.theme.gui;
import java.awt.BorderLayout;
import java.awt.event.*;
import java.beans.PropertyChangeEvent;
import javax.swing.*;
import javax.swing.table.TableColumn;
import docking.ActionContext;
import docking.action.ActionContextProvider;
import docking.widgets.table.GFilterTable;
import docking.widgets.table.GTable;
import generic.theme.Gui;
import generic.theme.IconValue;
import ghidra.util.Swing;
/**
* Icon Table for Theme Dialog
*/
public class ThemeIconTable extends JPanel implements ActionContextProvider {
private ThemeIconTableModel iconTableModel;
private IconValueEditor iconEditor = new IconValueEditor(this::iconValueChanged);
private GTable table;
private GFilterTable<IconValue> filterTable;
public ThemeIconTable() {
super(new BorderLayout());
iconTableModel = new ThemeIconTableModel();
filterTable = new GFilterTable<>(iconTableModel);
table = filterTable.getTable();
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
table.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
IconValue iconValue = filterTable.getSelectedRowObject();
if (iconValue != null) {
iconEditor.editValue(iconValue);
}
e.consume();
}
}
});
table.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) {
IconValue value = filterTable.getItemAt(e.getPoint());
int col = filterTable.getColumn(e.getPoint());
TableColumn column = table.getColumnModel().getColumn(col);
Object identifier = column.getIdentifier();
if ("Current Icon".equals(identifier) || "Id".equals(identifier)) {
iconEditor.editValue(value);
}
}
}
});
add(filterTable, BorderLayout.CENTER);
}
void iconValueChanged(PropertyChangeEvent event) {
// run later - don't rock the boat in the middle of a listener callback
Swing.runLater(() -> {
IconValue newValue = (IconValue) event.getNewValue();
Gui.setIcon(newValue);
});
}
/**
* Reloads all the values displayed in the table
*/
public void reloadAll() {
iconTableModel.reloadAll();
}
/**
* Returns the current values displayed in the table
*/
public void reloadCurrent() {
iconTableModel.reloadCurrent();
}
@Override
public ActionContext getActionContext(MouseEvent e) {
if (e.getSource() == table) {
IconValue currentValue = filterTable.getSelectedRowObject();
if (currentValue == null) {
return null;
}
String id = currentValue.getId();
IconValue themeValue = iconTableModel.getThemeValue(id);
return new ThemeTableContext<Icon>(currentValue, themeValue);
}
return null;
}
}

View file

@ -229,8 +229,16 @@ public class ThemeIconTableModel extends GDynamicColumnTableModel<IconValue, Obj
public String getFilterString(ResolvedIcon iconValue, Settings settings) { public String getFilterString(ResolvedIcon iconValue, Settings settings) {
return getValueText(iconValue); return getValueText(iconValue);
} }
} }
record ResolvedIcon(String id, String refId, Icon icon) {/**/} record ResolvedIcon(String id, String refId, Icon icon) {/**/}
/**
* Returns the original value for the id as defined by the current theme
* @param id the resource id to get a font value for
* @return the original value for the id as defined by the current theme
*/
public IconValue getThemeValue(String id) {
return themeValues.getIcon(id);
}
} }

View file

@ -0,0 +1,61 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package docking.theme.gui;
import docking.ActionContext;
import generic.theme.ThemeValue;
/**
* ActionContext for ThemeDialog tables
*
* @param <T> the resource type (Color, Font, or Icon)
*/
public class ThemeTableContext<T> extends ActionContext {
private ThemeValue<T> currentValue;
private ThemeValue<T> themeValue;
public ThemeTableContext(ThemeValue<T> currentValue, ThemeValue<T> themeValue) {
this.currentValue = currentValue;
this.themeValue = themeValue;
}
/**
* Returns the currentValue of the selected table row
* @return the currentValue of the selected table row
*/
public ThemeValue<T> getCurrentValue() {
return currentValue;
}
/**
* Returns the original theme value of the selected table row
* @return the original theme value of the selected table row
*/
public ThemeValue<T> getThemeValue() {
return themeValue;
}
/**
* Returns true if the current value is not the same as the original theme value for the
* selected table row
* @return true if the current value is not the same as the original theme value for the
* selected table row
*/
public boolean isChanged() {
return !currentValue.equals(themeValue);
}
}

View file

@ -365,7 +365,7 @@ public class ConditionTestPanel extends JPanel {
public TestPanel(ConditionTester conditionTest) { public TestPanel(ConditionTester conditionTest) {
super(new PairLayout()); super(new PairLayout());
backgroundColor = getBackground(); backgroundColor = getBackground();
selectedColor = Color.LIGHT_GRAY; selectedColor = Palette.LIGHT_GRAY;
this.test = conditionTest; this.test = conditionTest;
checkbox = new GCheckBox(); checkbox = new GCheckBox();
checkbox.setSelected(true); checkbox.setSelected(true);

View file

@ -25,6 +25,7 @@ import docking.DialogComponentProvider;
import docking.DockingUtils; import docking.DockingUtils;
import docking.widgets.label.GDLabel; import docking.widgets.label.GDLabel;
import docking.widgets.label.GLabel; import docking.widgets.label.GLabel;
import generic.theme.GThemeDefaults.Colors.Messages;
import ghidra.framework.OperatingSystem; import ghidra.framework.OperatingSystem;
import ghidra.framework.Platform; import ghidra.framework.Platform;
@ -87,7 +88,7 @@ public class MultiLineInputDialog extends DialogComponentProvider {
Font smallerFont = font.deriveFont(12F); Font smallerFont = font.deriveFont(12F);
Font smallItalicFont = smallerFont.deriveFont(Font.ITALIC); Font smallItalicFont = smallerFont.deriveFont(Font.ITALIC);
hintLabel.setFont(smallItalicFont); hintLabel.setFont(smallItalicFont);
hintLabel.setForeground(Color.LIGHT_GRAY); hintLabel.setForeground(Messages.HINT);
dataPanel.add(messageLabel, BorderLayout.NORTH); dataPanel.add(messageLabel, BorderLayout.NORTH);
dataPanel.add(new JScrollPane(inputTextArea), BorderLayout.CENTER); dataPanel.add(new JScrollPane(inputTextArea), BorderLayout.CENTER);

View file

@ -23,6 +23,7 @@ import javax.swing.JTextField;
import javax.swing.text.*; import javax.swing.text.*;
import docking.util.GraphicsUtils; import docking.util.GraphicsUtils;
import generic.theme.GThemeDefaults.Colors.Messages;
public class HexOrDecimalInput extends JTextField { public class HexOrDecimalInput extends JTextField {
private boolean isHexMode = false; private boolean isHexMode = false;
@ -130,7 +131,7 @@ public class HexOrDecimalInput extends JTextField {
Font font = new Font("Monospaced", Font.PLAIN, 10); Font font = new Font("Monospaced", Font.PLAIN, 10);
Font savedFont = g.getFont(); Font savedFont = g.getFont();
g.setFont(font); g.setFont(font);
g.setColor(Color.LIGHT_GRAY); g.setColor(Messages.HINT);
FontMetrics fontMetrics = getFontMetrics(font); FontMetrics fontMetrics = getFontMetrics(font);
String mode = isHexMode ? "Hex" : "Dec"; String mode = isHexMode ? "Hex" : "Dec";
int stringWidth = fontMetrics.stringWidth(mode); int stringWidth = fontMetrics.stringWidth(mode);

View file

@ -15,35 +15,19 @@
*/ */
package docking.widgets.textfield; package docking.widgets.textfield;
import java.awt.Color; import java.awt.*;
import java.awt.Dimension; import java.awt.event.*;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.math.BigInteger; import java.math.BigInteger;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import javax.swing.JComponent; import javax.swing.*;
import javax.swing.JTextField; import javax.swing.event.*;
import javax.swing.ToolTipManager; import javax.swing.text.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.AbstractDocument;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.DocumentFilter;
import docking.DockingUtils; import docking.DockingUtils;
import docking.util.GraphicsUtils; import docking.util.GraphicsUtils;
import generic.theme.GThemeDefaults.Colors.Messages;
import ghidra.util.SystemUtilities; import ghidra.util.SystemUtilities;
/** /**
@ -699,7 +683,7 @@ public class IntegerTextField {
Font savedFont = g.getFont(); Font savedFont = g.getFont();
g.setFont(hintFont); g.setFont(hintFont);
g.setColor(Color.LIGHT_GRAY); g.setColor(Messages.HINT);
Dimension size = getSize(); Dimension size = getSize();
Insets insets = getInsets(); Insets insets = getInsets();

View file

@ -31,6 +31,7 @@ import javax.swing.JTextField;
import javax.swing.event.*; import javax.swing.event.*;
import docking.util.GraphicsUtils; import docking.util.GraphicsUtils;
import generic.theme.GThemeDefaults.Colors.Messages;
import ghidra.util.SystemUtilities; import ghidra.util.SystemUtilities;
/** /**
@ -234,7 +235,7 @@ public class LocalDateTextField {
Font font = new Font("Monospaced", Font.PLAIN, 10); Font font = new Font("Monospaced", Font.PLAIN, 10);
Font savedFont = g.getFont(); Font savedFont = g.getFont();
g.setFont(font); g.setFont(font);
g.setColor(Color.LIGHT_GRAY); g.setColor(Messages.HINT);
FontMetrics fontMetrics = getFontMetrics(font); FontMetrics fontMetrics = getFontMetrics(font);
String label = isMonthMode ? MONTH_LABEL : DAY_LABEL; String label = isMonthMode ? MONTH_LABEL : DAY_LABEL;
int stringWidth = fontMetrics.stringWidth(label); int stringWidth = fontMetrics.stringWidth(label);

View file

@ -32,12 +32,13 @@ import docking.widgets.OptionDialog;
import docking.widgets.SelectFromListDialog; import docking.widgets.SelectFromListDialog;
import docking.widgets.dialogs.InputDialog; import docking.widgets.dialogs.InputDialog;
import generic.theme.*; import generic.theme.*;
import generic.theme.GThemeDefaults.Colors.Palette;
import generic.theme.builtin.MetalTheme; import generic.theme.builtin.MetalTheme;
import generic.theme.builtin.NimbusTheme; import generic.theme.builtin.NimbusTheme;
public class ThemeUtilsTest extends AbstractDockingTest { public class ThemeUtilsTest extends AbstractDockingTest {
private Color testColor = Color.RED; private Color testColor = Palette.RED;
@Before @Before
public void setup() { public void setup() {

View file

@ -149,4 +149,9 @@ public class ColorValue extends ThemeValue<Color> {
return outputString; return outputString;
} }
@Override
public void makeCurrentValue() {
Gui.setColor(this);
}
} }

View file

@ -223,4 +223,9 @@ public class FontValue extends ThemeValue<Font> {
return "PLAIN"; return "PLAIN";
} }
@Override
public void makeCurrentValue() {
Gui.setFont(this);
}
} }

View file

@ -146,4 +146,9 @@ public class IconValue extends ThemeValue<Icon> {
return iconToString(value); return iconToString(value);
} }
@Override
public void makeCurrentValue() {
Gui.setIcon(this);
}
} }

View file

@ -202,4 +202,9 @@ public abstract class ThemeValue<T> implements Comparable<ThemeValue<T>> {
return name + " (" + id + ", " + referenceId + ")"; return name + " (" + id + ", " + referenceId + ")";
} }
/**
* Makes this value the current value for the application
*/
public abstract void makeCurrentValue();
} }

View file

@ -17,6 +17,7 @@ package generic.theme.laf;
import java.awt.Component; import java.awt.Component;
import java.awt.Font; import java.awt.Font;
import java.util.Objects;
import generic.theme.Gui; import generic.theme.Gui;
import ghidra.util.datastruct.WeakDataStructureFactory; import ghidra.util.datastruct.WeakDataStructureFactory;
@ -54,7 +55,10 @@ public class ComponentFontRegistry {
public void updateComponentFonts() { public void updateComponentFonts() {
Font font = Gui.getFont(fontId); Font font = Gui.getFont(fontId);
for (Component component : components) { for (Component component : components) {
Font existingFont = component.getFont();
if (!Objects.equals(existingFont, font)) {
component.setFont(font); component.setFont(font);
} }
} }
} }
}

View file

@ -105,7 +105,7 @@ public abstract class LookAndFeelManager {
updateComponentUis(); updateComponentUis();
} }
private void updateAllRegisteredComponentFonts() { protected void updateAllRegisteredComponentFonts() {
for (ComponentFontRegistry register : fontRegistryMap.values()) { for (ComponentFontRegistry register : fontRegistryMap.values()) {
register.updateComponentFonts(); register.updateComponentFonts();
} }
@ -178,21 +178,12 @@ public abstract class LookAndFeelManager {
Font font = Gui.getFont(javaFontId); Font font = Gui.getFont(javaFontId);
defaults.put(javaFontId, new FontUIResource(font)); defaults.put(javaFontId, new FontUIResource(font));
} }
updateComponentFonts(changedJavaFontIds);
updateComponentUis(); updateComponentUis();
} }
updateAllRegisteredComponentFonts();
repaintAll(); repaintAll();
} }
protected void updateComponentFonts(Set<String> changedFontIds) {
for (String javaFontId : changedFontIds) {
ComponentFontRegistry register = fontRegistryMap.get(javaFontId);
if (register != null) {
register.updateComponentFonts();
}
}
}
protected void updateComponentUis() { protected void updateComponentUis() {
for (Window window : Window.getWindows()) { for (Window window : Window.getWindows()) {
SwingUtilities.updateComponentTreeUI(window); SwingUtilities.updateComponentTreeUI(window);

View file

@ -48,8 +48,8 @@ public class NimbusLookAndFeelManager extends LookAndFeelManager {
public void fontsChanged(Set<String> affectedJavaIds) { public void fontsChanged(Set<String> affectedJavaIds) {
if (!affectedJavaIds.isEmpty()) { if (!affectedJavaIds.isEmpty()) {
reinstallNimubus(); reinstallNimubus();
updateComponentFonts(affectedJavaIds);
} }
updateAllRegisteredComponentFonts();
repaintAll(); repaintAll();
} }

View file

@ -21,10 +21,10 @@ import java.io.IOException;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.net.URL; import java.net.URL;
import javax.swing.Icon;
import javax.swing.ImageIcon; import javax.swing.ImageIcon;
import generic.Images; import generic.Images;
import generic.theme.GIcon;
import generic.util.image.ImageUtils; import generic.util.image.ImageUtils;
import ghidra.util.Msg; import ghidra.util.Msg;
@ -37,18 +37,19 @@ import ghidra.util.Msg;
*/ */
public class IconProvider { public class IconProvider {
private GIcon icon; private Icon icon;
private URL url; private URL url;
private URL tempUrl; private URL tempUrl;
private boolean tempFileFailed; private boolean tempFileFailed;
public IconProvider(GIcon icon, URL url) { public IconProvider(Icon icon, URL url) {
this.icon = icon; this.icon = icon;
this.url = url; this.url = url;
} }
public Image getImage() { public Image getImage() {
return icon.getImageIcon().getImage(); ImageIcon imageIcon = ResourceManager.getImageIcon(icon);
return imageIcon.getImage();
} }
public boolean isInvalid() { public boolean isInvalid() {
@ -96,7 +97,7 @@ public class IconProvider {
try { try {
File imageFile = File.createTempFile("temp.help.icon", null); File imageFile = File.createTempFile("temp.help.icon", null);
imageFile.deleteOnExit(); // don't let this linger imageFile.deleteOnExit(); // don't let this linger
ImageIcon imageIcon = icon.getImageIcon(); ImageIcon imageIcon = ResourceManager.getImageIcon(icon);
ImageUtils.writeFile(imageIcon.getImage(), imageFile); ImageUtils.writeFile(imageIcon.getImage(), imageFile);
return imageFile.toURI().toURL(); return imageFile.toURI().toURL();
} }

View file

@ -113,7 +113,7 @@ public class Icons {
return null; return null;
} }
GIcon icon = getIconByFieldName(fieldName); Icon icon = getIconByFieldName(fieldName);
if (icon == null) { if (icon == null) {
return null; return null;
} }
@ -165,12 +165,12 @@ public class Icons {
return fieldName; return fieldName;
} }
private static GIcon getIconByFieldName(String fieldName) { private static Icon getIconByFieldName(String fieldName) {
try { try {
Field field = Icons.class.getField(fieldName); Field field = Icons.class.getField(fieldName);
Object object = field.get(Icons.class); Object object = field.get(Icons.class);
GIcon icon = (GIcon) object; Icon icon = (Icon) object;
return icon; return icon;
} }
catch (Exception e) { catch (Exception e) {
@ -180,16 +180,14 @@ public class Icons {
} }
} }
private static URL getUrlFromIcon(GIcon icon) { private static URL getUrlFromIcon(Icon icon) {
if (icon == null) { if (icon instanceof GIcon gIcon) {
return null; URL url = gIcon.getUrl();
}
URL url = icon.getUrl();
if (url != null) { if (url != null) {
return url; return url;
} }
Msg.debug(Icons.class, "Unable to get URL for icon"); Msg.debug(Icons.class, "Unable to get URL for icon");
}
return null; return null;
} }

View file

@ -24,6 +24,7 @@ import java.net.URL;
import java.util.*; import java.util.*;
import javax.swing.Icon; import javax.swing.Icon;
import javax.swing.JLabel;
import javax.swing.plaf.UIResource; import javax.swing.plaf.UIResource;
import org.junit.Before; import org.junit.Before;
@ -343,6 +344,17 @@ public class GuiTest {
assertEquals(expected, Gui.getApplicationDarkDefaults()); assertEquals(expected, Gui.getApplicationDarkDefaults());
} }
@Test
public void testRegisterFont() {
Gui.setFont(new FontValue("font.test", SMALL_FONT));
JLabel label = new JLabel("Test");
assertNotEquals(SMALL_FONT, label.getFont());
Gui.registerFont(label, "font.test");
assertEquals(SMALL_FONT, label.getFont());
Gui.setFont(new FontValue("font.test", FONT));
assertEquals(FONT, label.getFont());
}
private void assertColor(Color color, GColor gColor) { private void assertColor(Color color, GColor gColor) {
if (color.getRGB() != gColor.getRGB()) { if (color.getRGB() != gColor.getRGB()) {
fail("RGB values don't match! Expected " + color + " but got " + gColor); fail("RGB values don't match! Expected " + color + " but got " + gColor);

View file

@ -27,7 +27,6 @@ import com.google.common.base.Function;
import docking.DockingUtils; import docking.DockingUtils;
import docking.DockingWindowManager; import docking.DockingWindowManager;
import docking.actions.KeyBindingUtils; import docking.actions.KeyBindingUtils;
import docking.help.HelpService;
import docking.widgets.EmptyBorderButton; import docking.widgets.EmptyBorderButton;
import docking.widgets.PopupWindow; import docking.widgets.PopupWindow;
import docking.widgets.label.GIconLabel; import docking.widgets.label.GIconLabel;

View file

@ -88,7 +88,7 @@ public class AlgorithmTestSteppingVertex<V> extends AbstractTestVertex
private void buildShapes() { private void buildShapes() {
defaultShape = buildCircleShape(Color.LIGHT_GRAY, "default"); defaultShape = buildCircleShape(Palette.LIGHT_GRAY, "default");
defaultWithPathShape = defaultWithPathShape =
buildCircleShape(new GColor("color.palette.yellowgreen"), "default; was in path"); buildCircleShape(new GColor("color.palette.yellowgreen"), "default; was in path");
scheduledShape = buildCircleShape(new GColor("color.palette.khaki"), "scheduled"); scheduledShape = buildCircleShape(new GColor("color.palette.khaki"), "scheduled");

View file

@ -28,7 +28,6 @@ import generic.theme.GThemeDefaults.Colors.Palette;
import ghidra.framework.options.Options; import ghidra.framework.options.Options;
import ghidra.framework.options.ToolOptions; import ghidra.framework.options.ToolOptions;
import ghidra.util.HelpLocation; import ghidra.util.HelpLocation;
import ghidra.util.WebColors;
public class GraphDisplayOptionsTest { public class GraphDisplayOptionsTest {
@ -154,9 +153,9 @@ public class GraphDisplayOptionsTest {
vertex.setVertexType("V1"); vertex.setVertexType("V1");
assertEquals(Palette.RED, options.getVertexColor(vertex)); assertEquals(Palette.RED, options.getVertexColor(vertex));
vertex.setAttribute("Color", WebColors.toString(Palette.BLUE)); vertex.setAttribute("Color", Palette.BLUE.toString());
assertEquals(Palette.BLUE, options.getVertexColor(vertex)); assertEquals(Palette.BLUE.getRGB(), options.getVertexColor(vertex).getRGB());
} }
@Test @Test
@ -184,9 +183,9 @@ public class GraphDisplayOptionsTest {
edge.setEdgeType("E1"); edge.setEdgeType("E1");
assertEquals(Palette.RED, options.getEdgeColor(edge)); assertEquals(Palette.RED, options.getEdgeColor(edge));
edge.setAttribute("Color", WebColors.toString(Palette.BLUE)); edge.setAttribute("Color", Palette.BLUE.toString());
assertEquals(Palette.BLUE, options.getEdgeColor(edge)); assertEquals(Palette.BLUE.getRGB(), options.getEdgeColor(edge).getRGB());
} }
@Test @Test
@ -269,7 +268,7 @@ public class GraphDisplayOptionsTest {
AttributedVertex vertex = new AttributedVertex("Foo"); AttributedVertex vertex = new AttributedVertex("Foo");
vertex.setVertexType("V1"); vertex.setVertexType("V1");
assertEquals(Palette.BLUE, options.getVertexColor(vertex)); assertEquals(Palette.BLUE.getRGB(), options.getVertexColor(vertex).getRGB());
Options graphDisplayOptions = toolOptions.getOptions(options.getRootOptionsName()); Options graphDisplayOptions = toolOptions.getOptions(options.getRootOptionsName());
Options vertexColorOptions = graphDisplayOptions.getOptions("Vertex Colors"); Options vertexColorOptions = graphDisplayOptions.getOptions("Vertex Colors");

View file

@ -74,6 +74,11 @@ public class DefaultHelpService implements HelpService {
return false; return false;
} }
@Override
public void reload() {
// no-op
}
private void displayHelpInfo(Object helpObj) { private void displayHelpInfo(Object helpObj) {
String msg = getHelpInfo(helpObj); String msg = getHelpInfo(helpObj);
Msg.showInfo(this, null, "Help Info", msg); Msg.showInfo(this, null, "Help Info", msg);

View file

@ -46,6 +46,7 @@ public class GHelpBroker extends DefaultHelpBroker {
protected JEditorPane htmlEditorPane; protected JEditorPane htmlEditorPane;
private Window activationWindow; private Window activationWindow;
private boolean initialized;
/** /**
* Construct a new GhidraHelpBroker. * Construct a new GhidraHelpBroker.
@ -89,6 +90,22 @@ public class GHelpBroker extends DefaultHelpBroker {
} }
} }
public void reload() {
clearHighlights();
initialized = false;
if (isDisplayed()) {
setDisplayed(false);
setDisplayed(true);
}
}
private void clearHighlights() {
TextHelpModel helpModel = (TextHelpModel) getCustomHelpModel();
if (helpModel != null) {
helpModel.removeAllHighlights();
}
}
protected void showNavigationAid(URL url) { protected void showNavigationAid(URL url) {
// this base class does not have a navigation aid // this base class does not have a navigation aid
} }
@ -134,7 +151,7 @@ public class GHelpBroker extends DefaultHelpBroker {
} }
private void initializeScreenDevice() { private void initializeScreenDevice() {
if (isInitialized()) { if (initialized) {
return; return;
} }
@ -179,20 +196,21 @@ public class GHelpBroker extends DefaultHelpBroker {
initializeUIComponents(contentPane); initializeUIComponents(contentPane);
} }
private boolean isInitialized() {
return htmlEditorPane != null;
}
private void initializeUIComponents(Container contentPane) { private void initializeUIComponents(Container contentPane) {
if (isInitialized()) { if (initialized) {
return;// already initialized return;// already initialized
} }
Component[] components = contentPane.getComponents(); Component[] components = contentPane.getComponents();
JHelp jHelp = (JHelp) components[0]; JHelp jHelp = (JHelp) components[0];
addCustomToolbarItems(jHelp);
JHelpContentViewer contentViewer = jHelp.getContentViewer(); JHelpContentViewer contentViewer = jHelp.getContentViewer();
JEditorPane activeHtmlPane = getHTMLEditorPane(contentViewer);
if (activeHtmlPane == htmlEditorPane && initialized) {
return; // already initialized
}
addCustomToolbarItems(jHelp);
htmlEditorPane = getHTMLEditorPane(contentViewer); htmlEditorPane = getHTMLEditorPane(contentViewer);
// just creating the search wires everything together // just creating the search wires everything together
@ -203,6 +221,7 @@ public class GHelpBroker extends DefaultHelpBroker {
} }
installActions(jHelp); installActions(jHelp);
initialized = true;
} }
protected void installHelpSearcher(JHelp jHelp, HelpModel helpModel) { protected void installHelpSearcher(JHelp jHelp, HelpModel helpModel) {

View file

@ -26,7 +26,6 @@ import java.util.List;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import javax.swing.ImageIcon;
import javax.swing.JEditorPane; import javax.swing.JEditorPane;
import javax.swing.event.HyperlinkEvent; import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener; import javax.swing.event.HyperlinkListener;
@ -496,8 +495,7 @@ public class GHelpHTMLEditorKit extends HTMLEditorKit {
return null; return null;
} }
ImageIcon imageIcon = iconProvider.getIcon(); this.image = iconProvider.getImage();
this.image = imageIcon.getImage();
URL url = iconProvider.getOrCreateUrl(); URL url = iconProvider.getOrCreateUrl();
return url; return url;

View file

@ -113,4 +113,9 @@ public interface HelpService {
* initializing * initializing
*/ */
public boolean helpExists(); public boolean helpExists();
/**
* Called when a major system even happens, such as changing the system theme.
*/
public void reload();
} }

View file

@ -18,6 +18,7 @@ apply from: "$rootProject.projectDir/gradle/javaProject.gradle"
apply from: "$rootProject.projectDir/gradle/jacocoProject.gradle" apply from: "$rootProject.projectDir/gradle/jacocoProject.gradle"
apply from: "$rootProject.projectDir/gradle/javaTestProject.gradle" apply from: "$rootProject.projectDir/gradle/javaTestProject.gradle"
apply from: "$rootProject.projectDir/gradle/javadoc.gradle" apply from: "$rootProject.projectDir/gradle/javadoc.gradle"
apply plugin: 'eclipse' apply plugin: 'eclipse'
eclipse.project.name = 'Framework Project' eclipse.project.name = 'Framework Project'

View file

@ -9,6 +9,12 @@
Module.manifest||GHIDRA||||END| Module.manifest||GHIDRA||||END|
data/ExtensionPoint.manifest||GHIDRA||||END| data/ExtensionPoint.manifest||GHIDRA||||END|
data/project.theme.properties||GHIDRA||||END| data/project.theme.properties||GHIDRA||||END|
src/main/help/help/TOC_Source.xml||GHIDRA||||END|
src/main/help/help/topics/Theming/Theming.htm||GHIDRA||||END|
src/main/help/help/topics/Theming/images/ColorEditor.png||GHIDRA||||END|
src/main/help/help/topics/Theming/images/FontEditor.png||GHIDRA||||END|
src/main/help/help/topics/Theming/images/IconEditor.png||GHIDRA||||END|
src/main/help/help/topics/Theming/images/ThemeDialog.png||GHIDRA||||END|
src/main/java/ghidra/framework/cmd/package.html||GHIDRA||reviewed||END| src/main/java/ghidra/framework/cmd/package.html||GHIDRA||reviewed||END|
src/main/java/ghidra/framework/data/package.html||GHIDRA||reviewed||END| src/main/java/ghidra/framework/data/package.html||GHIDRA||reviewed||END|
src/main/java/ghidra/framework/model/package.html||GHIDRA||||END| src/main/java/ghidra/framework/model/package.html||GHIDRA||||END|

View file

@ -23,6 +23,7 @@ import ghidra.framework.main.ApplicationLevelOnlyPlugin;
import ghidra.framework.main.UtilityPluginPackage; import ghidra.framework.main.UtilityPluginPackage;
import ghidra.framework.plugintool.*; import ghidra.framework.plugintool.*;
import ghidra.framework.plugintool.util.PluginStatus; import ghidra.framework.plugintool.util.PluginStatus;
import ghidra.util.HelpLocation;
//@formatter:off //@formatter:off
@PluginInfo( @PluginInfo(
@ -49,31 +50,35 @@ public class ThemeManagerPlugin extends Plugin implements ApplicationLevelOnlyPl
new ActionBuilder("Edit Theme", owner) new ActionBuilder("Edit Theme", owner)
.menuPath("Edit", "Theme") .menuPath("Edit", "Theme")
.menuGroup(group, "1") .menuGroup(group, "1")
.helpLocation(new HelpLocation("Theming", "Edit_Theme"))
.onAction(e -> ThemeDialog.editTheme()) .onAction(e -> ThemeDialog.editTheme())
.buildAndInstall(tool); .buildAndInstall(tool);
new ActionBuilder("Reset To Default", owner) new ActionBuilder("Reset", owner)
.menuPath("Edit", themeSubMenu, "Reset To Default") .menuPath("Edit", themeSubMenu, "Reset Theme Values")
.menuGroup(group, "2") .menuGroup(group, "2")
.helpLocation(new HelpLocation("Theming", "Reset_Theme_Values"))
.onAction(e -> ThemeUtils.resetThemeToDefault()) .onAction(e -> ThemeUtils.resetThemeToDefault())
.buildAndInstall(tool); .buildAndInstall(tool);
new ActionBuilder("Import Theme", owner) new ActionBuilder("Import Theme", owner)
.menuPath("Edit", themeSubMenu, "Import...") .menuPath("Edit", themeSubMenu, "Import...")
.menuGroup(group, "3") .menuGroup(group, "3")
.helpLocation(new HelpLocation("Theming", "Import_Theme"))
.onAction(e -> ThemeUtils.importTheme()) .onAction(e -> ThemeUtils.importTheme())
.buildAndInstall(tool); .buildAndInstall(tool);
new ActionBuilder("Export Theme", owner) new ActionBuilder("Export Theme", owner)
.menuPath("Edit", themeSubMenu, "Export...") .menuPath("Edit", themeSubMenu, "Export...")
.menuGroup(group, "4") .menuGroup(group, "4")
.helpLocation(new HelpLocation("Theming", "Export_Theme"))
.onAction(e -> ThemeUtils.exportTheme()) .onAction(e -> ThemeUtils.exportTheme())
.buildAndInstall(tool); .buildAndInstall(tool);
new ActionBuilder("Delete Theme", owner) new ActionBuilder("Delete Theme", owner)
.menuPath("Edit", themeSubMenu, "Delete...") .menuPath("Edit", themeSubMenu, "Delete...")
.menuGroup(group, "5") .menuGroup(group, "5")
// .enabledWhen(e -> Gui.getActiveTheme() instanceof FileGTheme) .helpLocation(new HelpLocation("Theming", "Delete_Theme"))
.onAction(e -> ThemeUtils.deleteTheme()) .onAction(e -> ThemeUtils.deleteTheme())
.buildAndInstall(tool); .buildAndInstall(tool);

View file

@ -31,6 +31,7 @@ import docking.tool.ToolConstants;
import docking.widgets.checkbox.GCheckBox; import docking.widgets.checkbox.GCheckBox;
import docking.widgets.list.ListPanel; import docking.widgets.list.ListPanel;
import generic.theme.GThemeDefaults.Colors; import generic.theme.GThemeDefaults.Colors;
import generic.theme.GThemeDefaults.Colors.Palette;
import ghidra.framework.ToolUtils; import ghidra.framework.ToolUtils;
import ghidra.framework.model.ToolTemplate; import ghidra.framework.model.ToolTemplate;
import ghidra.util.HelpLocation; import ghidra.util.HelpLocation;
@ -183,7 +184,7 @@ class ImportGhidraToolsDialog extends DialogComponentProvider {
while (itr.hasNext()) { while (itr.hasNext()) {
tools[count] = itr.next(); tools[count] = itr.next();
checkboxes[count] = new GCheckBox(tools[count], false); checkboxes[count] = new GCheckBox(tools[count], false);
checkboxes[count].setBackground(Color.LIGHT_GRAY); checkboxes[count].setBackground(Palette.LIGHT_GRAY);
count++; count++;
} }

View file

@ -15,15 +15,16 @@
*/ */
package ghidra.framework.main; package ghidra.framework.main;
import java.awt.*; import java.awt.BorderLayout;
import java.awt.Component;
import java.util.*; import java.util.*;
import java.util.List;
import javax.swing.*; import javax.swing.*;
import docking.DialogComponentProvider; import docking.DialogComponentProvider;
import docking.tool.ToolConstants; import docking.tool.ToolConstants;
import docking.widgets.table.*; import docking.widgets.table.*;
import generic.theme.GThemeDefaults.Colors.Palette;
import ghidra.framework.data.ContentHandler; import ghidra.framework.data.ContentHandler;
import ghidra.framework.model.*; import ghidra.framework.model.*;
import ghidra.framework.project.tool.GhidraToolTemplate; import ghidra.framework.project.tool.GhidraToolTemplate;
@ -239,7 +240,9 @@ class SetToolAssociationsDialog extends DialogComponentProvider {
private class ContentHandlerComparator implements Comparator<ToolAssociationInfo> { private class ContentHandlerComparator implements Comparator<ToolAssociationInfo> {
@Override @Override
public int compare(ToolAssociationInfo o1, ToolAssociationInfo o2) { public int compare(ToolAssociationInfo o1, ToolAssociationInfo o2) {
return o1.getContentHandler().getContentType().compareTo( return o1.getContentHandler()
.getContentType()
.compareTo(
o2.getContentHandler().getContentType()); o2.getContentHandler().getContentType());
} }
} }
@ -298,7 +301,7 @@ class SetToolAssociationsDialog extends DialogComponentProvider {
return; return;
} }
renderer.setForeground(Color.LIGHT_GRAY); renderer.setForeground(Palette.LIGHT_GRAY);
Icon icon = null; Icon icon = null;
if (template.getName().equals(info.getAssociatedToolName())) { if (template.getName().equals(info.getAssociatedToolName())) {

View file

@ -19,12 +19,14 @@ import java.awt.Color;
import javax.swing.JFrame; import javax.swing.JFrame;
import generic.theme.GThemeDefaults.Colors.Palette;
/*package*/ abstract class AbstractSearchScreenShots extends GhidraScreenShotGenerator { /*package*/ abstract class AbstractSearchScreenShots extends GhidraScreenShotGenerator {
protected static final Color YELLOW_ORANGE = new Color(155, 150, 50); protected static final Color YELLOW_ORANGE = Palette.getColor("darkkhaki");
protected static final Color BLUE_GREEN = new Color(0, 128, 64); protected static final Color BLUE_GREEN = Palette.GREEN;
protected static final Color DARK_BLUE = new Color(0, 0, 128); protected static final Color DARK_BLUE = Palette.getColor("navy");
protected static final Color DARK_GREEN = new Color(0, 128, 0); protected static final Color DARK_GREEN = Palette.getColor("darkgreen");
@Override @Override
protected String getHelpTopicName() { protected String getHelpTopicName() {

View file

@ -22,6 +22,8 @@ import java.util.concurrent.CountDownLatch;
import org.junit.Test; import org.junit.Test;
import docking.DialogComponentProvider; import docking.DialogComponentProvider;
import generic.theme.GThemeDefaults.Colors;
import generic.theme.GThemeDefaults.Colors.Palette;
import ghidra.framework.cmd.BackgroundCommand; import ghidra.framework.cmd.BackgroundCommand;
import ghidra.framework.model.DomainObject; import ghidra.framework.model.DomainObject;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
@ -39,27 +41,27 @@ public class AutoAnalysisPluginScreenShots extends GhidraScreenShotGenerator {
@Test @Test
public void testAutoAnalysis() { public void testAutoAnalysis() {
Color darkGreen = new Color(20, 154, 65); Color darkGreen = Palette.GREEN;
Color darkBlue = new Color(10, 62, 149); Color darkBlue = Palette.getColor("darkblue");
image = new BufferedImage(700, 400, BufferedImage.TYPE_INT_ARGB); image = new BufferedImage(700, 400, BufferedImage.TYPE_INT_ARGB);
Graphics g = image.getGraphics(); Graphics g = image.getGraphics();
g.setColor(Color.WHITE); g.setColor(Colors.BACKGROUND);
g.fillRect(0, 0, 700, 400); g.fillRect(0, 0, 700, 400);
drawText("(1) User Disassembles Code", Color.BLACK, new Point(160, 30), 24); drawText("(1) User Disassembles Code", Colors.FOREGROUND, new Point(160, 30), 24);
drawArrow(darkBlue, new Point(325, 35), new Point(325, 70)); drawArrow(darkBlue, new Point(325, 35), new Point(325, 70));
drawText("(new code)", darkGreen, new Point(270, 90), 24); drawText("(new code)", darkGreen, new Point(270, 90), 24);
drawText("(2) Function Analyzer", Color.BLACK, new Point(0, 150), 24); drawText("(2) Function Analyzer", Colors.FOREGROUND, new Point(0, 150), 24);
drawArrow(darkBlue, new Point(265, 82), new Point(180, 120)); drawArrow(darkBlue, new Point(265, 82), new Point(180, 120));
drawText("(new function)", darkGreen, new Point(100, 190), 24); drawText("(new function)", darkGreen, new Point(100, 190), 24);
drawText("(3) Stack Analyzer", Color.BLACK, new Point(10, 230), 24); drawText("(3) Stack Analyzer", Colors.FOREGROUND, new Point(10, 230), 24);
drawArrow(darkBlue, new Point(50, 155), new Point(50, 205)); drawArrow(darkBlue, new Point(50, 155), new Point(50, 205));
drawText("(4) Operand Analyzer", Color.BLACK, new Point(180, 290), 24); drawText("(4) Operand Analyzer", Colors.FOREGROUND, new Point(180, 290), 24);
drawArrow(darkBlue, new Point(300, 94), new Point(300, 260)); drawArrow(darkBlue, new Point(300, 94), new Point(300, 260));
drawText("(5) Data Reference Analyzer", Color.BLACK, new Point(280, 350), 24); drawText("(5) Data Reference Analyzer", Colors.FOREGROUND, new Point(280, 350), 24);
drawArrow(darkBlue, new Point(350, 94), new Point(490, 325)); drawArrow(darkBlue, new Point(350, 94), new Point(490, 325));
Point p1 = new Point(447, 355); Point p1 = new Point(447, 355);
@ -122,12 +124,7 @@ public class AutoAnalysisPluginScreenShots extends GhidraScreenShotGenerator {
monitor.initialize(100); monitor.initialize(100);
monitor.setProgress(65); monitor.setProgress(65);
monitor.setMessage("Applying Function Signatures"); monitor.setMessage("Applying Function Signatures");
runSwing(new Runnable() { runSwing(() -> invokeInstanceMethod("update", monitor));
@Override
public void run() {
invokeInstanceMethod("update", monitor);
}
});
start.countDown(); start.countDown();
try { try {

View file

@ -21,6 +21,7 @@ import java.util.List;
import org.junit.Test; import org.junit.Test;
import generic.theme.TempColorUtils;
import ghidra.GhidraOptions; import ghidra.GhidraOptions;
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin; import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
import ghidra.app.plugin.core.colorizer.ColorizingService; import ghidra.app.plugin.core.colorizer.ColorizingService;
@ -38,7 +39,7 @@ import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSet; import ghidra.program.model.address.AddressSet;
import ghidra.program.model.block.*; import ghidra.program.model.block.*;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitorAdapter; import ghidra.util.task.TaskMonitor;
public class BlockModelScreenShots extends GhidraScreenShotGenerator { public class BlockModelScreenShots extends GhidraScreenShotGenerator {
@ -95,16 +96,16 @@ public class BlockModelScreenShots extends GhidraScreenShotGenerator {
ColorizingService colorizer = tool.getService(ColorizingService.class); ColorizingService colorizer = tool.getService(ColorizingService.class);
Color c1 = new Color(0xE8F2FE); // note: 2 colors that look good together and are just used for this example
Color c1 = TempColorUtils.fromRgb(232, 242, 254); // alice blue;
Color c2 = new Color(170, 204, 245); Color c2 = TempColorUtils.fromRgb(170, 204, 245); // light sky blue
Color color = c1; Color color = c1;
BasicBlockModel basicBlockModel = new BasicBlockModel(program); BasicBlockModel basicBlockModel = new BasicBlockModel(program);
CodeBlockIterator iterator; CodeBlockIterator iterator;
try { try {
iterator = basicBlockModel.getCodeBlocksContaining(addressSet, iterator = basicBlockModel.getCodeBlocksContaining(addressSet,
TaskMonitorAdapter.DUMMY_MONITOR); TaskMonitor.DUMMY);
while (iterator.hasNext()) { while (iterator.hasNext()) {
CodeBlock block = iterator.next(); CodeBlock block = iterator.next();
@ -176,7 +177,8 @@ public class BlockModelScreenShots extends GhidraScreenShotGenerator {
if (fieldFactory.getFieldName().indexOf("XRef") != -1) { if (fieldFactory.getFieldName().indexOf("XRef") != -1) {
formatModel.removeFactory(row, col); formatModel.removeFactory(row, col);
} }
else if (fieldFactory.getFieldName().equals( else if (fieldFactory.getFieldName()
.equals(
EolCommentFieldFactory.FIELD_NAME)) { EolCommentFieldFactory.FIELD_NAME)) {
formatModel.removeFactory(row, col); formatModel.removeFactory(row, col);
} }

View file

@ -28,6 +28,7 @@ import docking.DialogComponentProvider;
import docking.action.DockingAction; import docking.action.DockingAction;
import docking.action.DockingActionIf; import docking.action.DockingActionIf;
import docking.widgets.fieldpanel.FieldPanel; import docking.widgets.fieldpanel.FieldPanel;
import generic.theme.GThemeDefaults.Colors.Java;
import ghidra.app.plugin.core.clipboard.*; import ghidra.app.plugin.core.clipboard.*;
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin; import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
import ghidra.app.plugin.core.codebrowser.CodeViewerProvider; import ghidra.app.plugin.core.codebrowser.CodeViewerProvider;
@ -74,7 +75,7 @@ public class ClipboardPluginScreenShots extends GhidraScreenShotGenerator {
captureListingCallMnemonic(start, end); captureListingCallMnemonic(start, end);
placeImagesSideBySide(image, menuImage); placeImagesSideBySide(image, menuImage);
drawBorder(Color.BLACK); drawBorder(Java.BORDER);
} }
private void cropCopyMenu() { private void cropCopyMenu() {
@ -149,9 +150,7 @@ public class ClipboardPluginScreenShots extends GhidraScreenShotGenerator {
Object listPanel = getInstanceField("listPanel", copySpecialDialog); Object listPanel = getInstanceField("listPanel", copySpecialDialog);
final JList<?> list = (JList<?>) getInstanceField("list", listPanel); final JList<?> list = (JList<?>) getInstanceField("list", listPanel);
runSwing(new Runnable() { runSwing(() -> {
@Override
public void run() {
ListModel<?> model = list.getModel(); ListModel<?> model = list.getModel();
int size = model.getSize(); int size = model.getSize();
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
@ -163,7 +162,6 @@ public class ClipboardPluginScreenShots extends GhidraScreenShotGenerator {
} }
throw new RuntimeException("Could not find 'Labels and Comments' copy action"); throw new RuntimeException("Could not find 'Labels and Comments' copy action");
}
}); });
waitForSwing(); waitForSwing();
} }

View file

@ -28,6 +28,9 @@ import org.junit.Test;
import docking.DockableComponent; import docking.DockableComponent;
import docking.widgets.fieldpanel.FieldPanel; import docking.widgets.fieldpanel.FieldPanel;
import generic.theme.GThemeDefaults.Colors;
import generic.theme.GThemeDefaults.Colors.Java;
import generic.theme.GThemeDefaults.Colors.Palette;
import ghidra.GhidraOptions; import ghidra.GhidraOptions;
import ghidra.app.cmd.comments.SetCommentCmd; import ghidra.app.cmd.comments.SetCommentCmd;
import ghidra.app.cmd.data.CreateDataCmd; import ghidra.app.cmd.data.CreateDataCmd;
@ -71,7 +74,7 @@ public class CodeBrowserPluginScreenShots extends GhidraScreenShotGenerator {
Rectangle cursor = getCursorBounds(); Rectangle cursor = getCursorBounds();
captureListingRange(0x0040be40, 0x0040be56, 600); captureListingRange(0x0040be40, 0x0040be56, 600);
drawBorder(Color.BLACK); drawBorder(Java.BORDER);
drawTextWithArrowNearOpenStructureIcon("Closed", cursor); drawTextWithArrowNearOpenStructureIcon("Closed", cursor);
@ -92,7 +95,7 @@ public class CodeBrowserPluginScreenShots extends GhidraScreenShotGenerator {
Rectangle cursor = getCursorBounds(); Rectangle cursor = getCursorBounds();
captureListingRange(0x0040be40, 0x0040be56, 600); captureListingRange(0x0040be40, 0x0040be56, 600);
drawBorder(Color.BLACK); drawBorder(Java.BORDER);
drawTextWithArrowNearOpenStructureIcon("Open", cursor); drawTextWithArrowNearOpenStructureIcon("Open", cursor);
} }
@ -102,7 +105,7 @@ public class CodeBrowserPluginScreenShots extends GhidraScreenShotGenerator {
// Make some room to draw our annotations (text and an arrow) // Make some room to draw our annotations (text and an arrow)
// //
Dimension whitespace = new Dimension(150, 10); Dimension whitespace = new Dimension(150, 10);
padImage(Color.WHITE, whitespace.height, whitespace.width, 10, 10); padImage(Colors.BACKGROUND, whitespace.height, whitespace.width, 10, 10);
// //
// Draw text inside of the newly padded space // Draw text inside of the newly padded space
@ -112,14 +115,14 @@ public class CodeBrowserPluginScreenShots extends GhidraScreenShotGenerator {
int textStartY = arrowStartY - 4;// up just a bit int textStartY = arrowStartY - 4;// up just a bit
Point textPoint = new Point(textStartX, textStartY); Point textPoint = new Point(textStartX, textStartY);
int size = 24; int size = 24;
Color textColor = Color.MAGENTA.darker(); Color textColor = Palette.PURPLE;
drawText(text, textColor, textPoint, size); drawText(text, textColor, textPoint, size);
// //
// Draw an arrow from the text above to the 'open structure' icon // Draw an arrow from the text above to the 'open structure' icon
// //
int arrowStartX = 60; int arrowStartX = 60;
Color arrowColor = Color.GREEN.darker(); Color arrowColor = Palette.GREEN;
Point arrowStart = new Point(arrowStartX, arrowStartY); Point arrowStart = new Point(arrowStartX, arrowStartY);
int addressFieldStartX = 40; int addressFieldStartX = 40;
int listingOffsetX = whitespace.width; int listingOffsetX = whitespace.width;
@ -205,14 +208,14 @@ public class CodeBrowserPluginScreenShots extends GhidraScreenShotGenerator {
captureListingRange(topAddr, bottomAddr, 600); captureListingRange(topAddr, bottomAddr, 600);
int padX = 100; int padX = 100;
padImage(Color.LIGHT_GRAY, 0, padX, 0, 0); padImage(Palette.LIGHT_GRAY, 0, padX, 0, 0);
int y = conditional.y + 10; int y = conditional.y + 10;
drawText("Conditional", Color.BLACK, new Point(10, y), 12); drawText("Conditional", Colors.FOREGROUND, new Point(10, y), 12);
drawText(" Jump", Color.BLACK, new Point(10, y + 15), 12); drawText(" Jump", Colors.FOREGROUND, new Point(10, y + 15), 12);
y = unconditional.y + 10; y = unconditional.y + 10;
drawText("Unconditional", Color.BLACK, new Point(10, y), 12); drawText("Unconditional", Colors.FOREGROUND, new Point(10, y), 12);
drawText(" Jump", Color.BLACK, new Point(10, y + 15), 12); drawText(" Jump", Colors.FOREGROUND, new Point(10, y + 15), 12);
} }
@ -288,7 +291,7 @@ public class CodeBrowserPluginScreenShots extends GhidraScreenShotGenerator {
int width = image.getWidth(null); int width = image.getWidth(null);
crop(new Rectangle(0, 0, width, 30)); crop(new Rectangle(0, 0, width, 30));
drawOval(new Color(107, 47, 109), drawOval(Palette.PURPLE,
new Rectangle(p.x - 13, p.y - 1, bounds.width + 26, bounds.height + 2), 4); new Rectangle(p.x - 13, p.y - 1, bounds.width + 26, bounds.height + 2), 4);
} }
@ -318,7 +321,7 @@ public class CodeBrowserPluginScreenShots extends GhidraScreenShotGenerator {
int y = p.y - 2; int y = p.y - 2;
int height = bounds.height + 12; int height = bounds.height + 12;
int width = bounds.width + 34; int width = bounds.width + 34;
Color color = new Color(120, 0, 64); Color color = Palette.PURPLE;
drawOval(color, new Rectangle(x, y, width, height), 5); drawOval(color, new Rectangle(x, y, width, height), 5);
int arrowHeadX = x + (width / 4); int arrowHeadX = x + (width / 4);

Some files were not shown because too many files have changed in this diff Show more