GP-1981 Theme help and module conversions
|
@ -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 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>
|
||||
|
||||
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.function.editor;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.event.*;
|
||||
import java.math.BigInteger;
|
||||
|
@ -28,6 +27,7 @@ import javax.swing.table.TableCellEditor;
|
|||
|
||||
import docking.widgets.combobox.GhidraComboBox;
|
||||
import docking.widgets.textfield.IntegerTextField;
|
||||
import generic.theme.GThemeDefaults.Colors.Palette;
|
||||
import ghidra.app.util.AddressInput;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressOutOfBoundsException;
|
||||
|
@ -44,12 +44,7 @@ class VarnodeLocationCellEditor extends AbstractCellEditor implements TableCellE
|
|||
private AddressInput addressInput;
|
||||
private IntegerTextField offsetInput;
|
||||
|
||||
private Comparator<Register> registerWrapperComparator = new Comparator<Register>() {
|
||||
@Override
|
||||
public int compare(Register r1, Register r2) {
|
||||
return r1.toString().compareToIgnoreCase(r2.toString());
|
||||
}
|
||||
};
|
||||
private Comparator<Register> registerWrapperComparator = (r1, r2) -> r1.toString().compareToIgnoreCase(r2.toString());
|
||||
private VarnodeInfo currentVarnode;
|
||||
private int maxRegisterSize;
|
||||
|
||||
|
@ -146,12 +141,7 @@ class VarnodeLocationCellEditor extends AbstractCellEditor implements TableCellE
|
|||
if (address != null) {
|
||||
addressInput.setAddress(address);
|
||||
}
|
||||
addressInput.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
stopCellEditing();
|
||||
}
|
||||
});
|
||||
addressInput.addActionListener(e -> stopCellEditing());
|
||||
return addressInput;
|
||||
}
|
||||
|
||||
|
@ -162,14 +152,9 @@ class VarnodeLocationCellEditor extends AbstractCellEditor implements TableCellE
|
|||
if (address != null) {
|
||||
offsetInput.setValue(address.getOffset());
|
||||
}
|
||||
offsetInput.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
stopCellEditing();
|
||||
}
|
||||
});
|
||||
offsetInput.addActionListener(e -> stopCellEditing());
|
||||
JComponent component = offsetInput.getComponent();
|
||||
component.setBorder(BorderFactory.createLineBorder(Color.GRAY, 1));
|
||||
component.setBorder(BorderFactory.createLineBorder(Palette.GRAY, 1));
|
||||
return component;
|
||||
}
|
||||
|
||||
|
@ -215,12 +200,7 @@ class VarnodeLocationCellEditor extends AbstractCellEditor implements TableCellE
|
|||
}
|
||||
});
|
||||
|
||||
combo.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
stopCellEditing();
|
||||
}
|
||||
});
|
||||
combo.addActionListener(e -> stopCellEditing());
|
||||
|
||||
return combo;
|
||||
}
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.function.editor;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.event.*;
|
||||
import java.math.BigInteger;
|
||||
|
@ -25,6 +24,7 @@ import javax.swing.*;
|
|||
import javax.swing.table.TableCellEditor;
|
||||
|
||||
import docking.widgets.textfield.IntegerTextField;
|
||||
import generic.theme.GThemeDefaults.Colors.Palette;
|
||||
|
||||
class VarnodeSizeCellEditor extends AbstractCellEditor implements TableCellEditor {
|
||||
|
||||
|
@ -70,14 +70,9 @@ class VarnodeSizeCellEditor extends AbstractCellEditor implements TableCellEdito
|
|||
};
|
||||
input.getComponent().addFocusListener(focusListener);
|
||||
}
|
||||
input.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
stopCellEditing();
|
||||
}
|
||||
});
|
||||
input.addActionListener(e -> stopCellEditing());
|
||||
JComponent component = input.getComponent();
|
||||
component.setBorder(BorderFactory.createLineBorder(Color.GRAY, 1));
|
||||
component.setBorder(BorderFactory.createLineBorder(Palette.GRAY, 1));
|
||||
return component;
|
||||
}
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ import javax.swing.*;
|
|||
import docking.widgets.OptionDialog;
|
||||
import docking.widgets.table.*;
|
||||
import generic.theme.GColor;
|
||||
import generic.theme.GThemeDefaults.Colors.Palette;
|
||||
import ghidra.app.cmd.register.SetRegisterCmd;
|
||||
import ghidra.app.events.ProgramSelectionPluginEvent;
|
||||
import ghidra.app.services.*;
|
||||
|
@ -492,7 +493,7 @@ class RegisterValueRange {
|
|||
|
||||
class RegisterValueRenderer extends GTableCellRenderer {
|
||||
|
||||
private Color defaultColor = Color.LIGHT_GRAY;
|
||||
private Color defaultColor = Palette.LIGHT_GRAY;
|
||||
|
||||
RegisterValueRenderer(JTable table) {
|
||||
setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 0));
|
||||
|
|
|
@ -15,10 +15,9 @@
|
|||
*/
|
||||
package ghidra.app.util.viewer.field;
|
||||
|
||||
import java.awt.Color;
|
||||
|
||||
import docking.widgets.fieldpanel.field.AttributedString;
|
||||
import generic.theme.GThemeDefaults.Colors.Messages;
|
||||
import generic.theme.GThemeDefaults.Colors.Palette;
|
||||
import ghidra.app.nav.Navigatable;
|
||||
import ghidra.app.services.GoToService;
|
||||
import ghidra.framework.plugintool.ServiceProvider;
|
||||
|
@ -76,7 +75,7 @@ public class AddressAnnotatedStringHandler implements AnnotatedStringHandler {
|
|||
buffer.append(string).append(" ");
|
||||
}
|
||||
|
||||
return new AttributedString(buffer.toString(), Color.LIGHT_GRAY,
|
||||
return new AttributedString(buffer.toString(), Palette.LIGHT_GRAY,
|
||||
prototypeString.getFontMetrics(0));
|
||||
}
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ import javax.swing.JComponent;
|
|||
import docking.widgets.fieldpanel.internal.FieldBackgroundColorManager;
|
||||
import docking.widgets.fieldpanel.internal.PaintContext;
|
||||
import docking.widgets.fieldpanel.support.*;
|
||||
import generic.theme.GThemeDefaults.Colors.Palette;
|
||||
import ghidra.app.util.viewer.format.FieldFormatModel;
|
||||
import ghidra.app.util.viewer.proxy.EmptyProxy;
|
||||
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,
|
||||
Rectangle clip, FieldBackgroundColorManager map, RowColLocation cursorLoc,
|
||||
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
|
||||
// bars drawn for inset data)
|
||||
|
|
|
@ -23,6 +23,7 @@ import javax.swing.JComponent;
|
|||
import docking.widgets.fieldpanel.internal.FieldBackgroundColorManager;
|
||||
import docking.widgets.fieldpanel.internal.PaintContext;
|
||||
import docking.widgets.fieldpanel.support.*;
|
||||
import generic.theme.GThemeDefaults.Colors.Palette;
|
||||
import ghidra.app.util.viewer.format.FieldFormatModel;
|
||||
import ghidra.app.util.viewer.proxy.EmptyProxy;
|
||||
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
|
||||
// there are vertical bars drawn for inset data)
|
||||
|
|
|
@ -35,6 +35,7 @@ import docking.widgets.fieldpanel.internal.FieldPanelCoordinator;
|
|||
import docking.widgets.fieldpanel.listener.FieldLocationListener;
|
||||
import docking.widgets.fieldpanel.support.FieldLocation;
|
||||
import docking.widgets.fieldpanel.support.ViewerPosition;
|
||||
import generic.theme.GThemeDefaults.Colors.Palette;
|
||||
import ghidra.GhidraOptions;
|
||||
import ghidra.app.nav.Navigatable;
|
||||
import ghidra.app.plugin.core.codebrowser.MarkerServiceBackgroundColorModel;
|
||||
|
@ -72,6 +73,8 @@ public class ListingCodeComparisonPanel
|
|||
extends CodeComparisonPanel<ListingComparisonFieldPanelCoordinator> implements
|
||||
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_SIDE_BY_SIDE = "DUAL_LISTING_SIDE_BY_SIDE";
|
||||
public static final String NAME = "DualListing";
|
||||
|
@ -1406,7 +1409,7 @@ public class ListingCodeComparisonPanel
|
|||
|
||||
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(padStr);
|
||||
}
|
||||
|
@ -1441,7 +1444,7 @@ public class ListingCodeComparisonPanel
|
|||
|
||||
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(padStr);
|
||||
}
|
||||
|
@ -1461,7 +1464,7 @@ public class ListingCodeComparisonPanel
|
|||
String padStr = HTMLUtilities.spaces(4);
|
||||
buf.append(padStr);
|
||||
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(padStr);
|
||||
return HTMLUtilities.wrapAsHTML(buf.toString());
|
||||
|
|
|
@ -15,11 +15,11 @@
|
|||
*/
|
||||
package ghidra.app.util.xml;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.util.*;
|
||||
|
||||
import javax.swing.ImageIcon;
|
||||
|
||||
import generic.theme.GThemeDefaults.Colors.Palette;
|
||||
import ghidra.app.cmd.function.*;
|
||||
import ghidra.app.services.DataTypeManagerService;
|
||||
import ghidra.app.util.NamespaceUtils;
|
||||
|
@ -129,7 +129,8 @@ class FunctionsXmlMgr {
|
|||
if (bt == null) {
|
||||
ImageIcon icon =
|
||||
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,
|
||||
"Library function");
|
||||
|
@ -680,7 +681,7 @@ class FunctionsXmlMgr {
|
|||
|
||||
private Parameter[] getRegisterParameters(Function function) {
|
||||
|
||||
ArrayList<Parameter> list = new ArrayList<Parameter>();
|
||||
ArrayList<Parameter> list = new ArrayList<>();
|
||||
Parameter[] params = function.getParameters();
|
||||
for (Parameter param : params) {
|
||||
if (param.isRegisterVariable()) {
|
||||
|
|
|
@ -15,9 +15,9 @@
|
|||
*/
|
||||
package ghidra.program.util;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.util.*;
|
||||
|
||||
import generic.theme.GThemeDefaults.Colors.Palette;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.data.GenericCallingConvention;
|
||||
|
@ -381,7 +381,7 @@ public class FunctionUtility {
|
|||
|
||||
String programStr =
|
||||
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(padStr);
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import javax.swing.ImageIcon;
|
|||
import javax.swing.JLabel;
|
||||
|
||||
import docking.widgets.table.GTableCellRenderingData;
|
||||
import generic.theme.GThemeDefaults.Colors;
|
||||
import ghidra.docking.settings.Settings;
|
||||
import ghidra.framework.plugintool.ServiceProvider;
|
||||
import ghidra.program.model.listing.Program;
|
||||
|
@ -75,7 +76,7 @@ public class MemoryTypeProgramLocationBasedTableColumn
|
|||
|
||||
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 onIcon = ResourceManager.loadImage("images/check.png");
|
||||
|
||||
|
|
|
@ -38,6 +38,14 @@ import ghidra.framework.main.FrontEndTool;
|
|||
import ghidra.util.Msg;
|
||||
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 {
|
||||
|
||||
private static final Color FG_COLOR_TEXT = Palette.getColor("color.palate.cornflowerblue");
|
||||
|
|
|
@ -25,6 +25,8 @@ import docking.*;
|
|||
import docking.action.DockingAction;
|
||||
import docking.action.MenuData;
|
||||
import docking.widgets.table.GFilterTable;
|
||||
import generic.theme.GColor;
|
||||
import generic.theme.GThemeDefaults.Colors;
|
||||
import ghidra.bitpatterns.info.*;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.bytesearch.DittedBitSequence;
|
||||
|
@ -35,6 +37,8 @@ import resources.ResourceManager;
|
|||
*/
|
||||
public abstract class ByteSequenceAnalyzerProvider extends DialogComponentProvider {
|
||||
|
||||
private static final Color BG_DISABLED = new GColor("color.bg.uneditable");
|
||||
|
||||
protected ByteSequenceTableModel byteSequenceTable;
|
||||
protected FunctionBitPatternsExplorerPlugin plugin;
|
||||
protected JPanel mainPanel;
|
||||
|
@ -175,9 +179,9 @@ public abstract class ByteSequenceAnalyzerProvider extends DialogComponentProvid
|
|||
}
|
||||
mergedSeqTextField.setText(merged.getHexString());
|
||||
bitsOfCheckField.setText(Integer.toString(merged.getNumFixedBits()));
|
||||
mergedSeqTextField.setBackground(Color.WHITE);
|
||||
bitsOfCheckField.setBackground(Color.WHITE);
|
||||
noteField.setBackground(Color.WHITE);
|
||||
mergedSeqTextField.setBackground(Colors.BACKGROUND);
|
||||
bitsOfCheckField.setBackground(Colors.BACKGROUND);
|
||||
noteField.setBackground(Colors.BACKGROUND);
|
||||
mergedToSend = true;
|
||||
}
|
||||
|
||||
|
@ -211,9 +215,9 @@ public abstract class ByteSequenceAnalyzerProvider extends DialogComponentProvid
|
|||
mergedInfo.setNote(note);
|
||||
plugin.addPattern(mergedInfo);
|
||||
plugin.updateClipboard();
|
||||
mergedSeqTextField.setBackground(Color.lightGray);
|
||||
bitsOfCheckField.setBackground(Color.LIGHT_GRAY);
|
||||
noteField.setBackground(Color.LIGHT_GRAY);
|
||||
mergedSeqTextField.setBackground(BG_DISABLED);
|
||||
bitsOfCheckField.setBackground(BG_DISABLED);
|
||||
noteField.setBackground(BG_DISABLED);
|
||||
mergedToSend = false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
[Defaults]
|
||||
|
||||
color.bg.byteviewer = color.bg
|
||||
color.bg.byteviewer.highlight = yellow
|
||||
|
||||
color.fg.byteviewer.novalue = blue
|
||||
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.unfocused.byteviewer = color.cursor.unfocused
|
||||
|
||||
|
||||
|
||||
[Dark Defaults]
|
||||
|
||||
color.bg.byteviewer.highlight = rgb(191, 191, 64) // olive
|
||||
|
||||
color.fg.byteviewer.novalue = DarkBlue
|
||||
color.fg.byteviewer.changed = indianRed
|
||||
color.cursor.focused.byteviewer.current = color.cursor.focused
|
||||
|
|
|
@ -29,6 +29,7 @@ import docking.widgets.fieldpanel.Layout;
|
|||
import docking.widgets.fieldpanel.field.Field;
|
||||
import docking.widgets.fieldpanel.listener.*;
|
||||
import docking.widgets.fieldpanel.support.*;
|
||||
import generic.theme.GColor;
|
||||
import ghidra.app.plugin.core.format.*;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.util.Msg;
|
||||
|
@ -885,7 +886,7 @@ public class ByteViewerComponent extends FieldPanel implements FieldMouseListene
|
|||
|
||||
private class ByteViewerBackgroundColorModel implements BackgroundColorModel {
|
||||
|
||||
private Color defaultBackgroundColor = Color.WHITE;
|
||||
private Color defaultBackgroundColor = new GColor("color.bg.byteviewer");
|
||||
|
||||
@Override
|
||||
public Color getBackgroundColor(BigInteger index) {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -16,18 +15,19 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.byteviewer;
|
||||
|
||||
import ghidra.app.util.HighlightProvider;
|
||||
import ghidra.app.util.viewer.field.FieldFactory;
|
||||
|
||||
import java.awt.Color;
|
||||
|
||||
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 {
|
||||
private static Highlight[] NO_HIGHLIGHTS = new Highlight[0];
|
||||
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,
|
||||
Class<? extends FieldFactory> fieldFactoryClass, int cursorTextOffset) {
|
||||
|
||||
|
|
|
@ -15,12 +15,12 @@
|
|||
*/
|
||||
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.ActionListener;
|
||||
import java.math.BigInteger;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import javax.swing.*;
|
||||
|
@ -31,6 +31,8 @@ import javax.swing.event.ChangeListener;
|
|||
import docking.DialogComponentProvider;
|
||||
import docking.widgets.checkbox.GCheckBox;
|
||||
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.DataFormatModel;
|
||||
import ghidra.app.util.AddressInput;
|
||||
|
@ -266,10 +268,10 @@ public class ByteViewerOptionsDialog extends DialogComponentProvider
|
|||
JCheckBox checkBox = entry.getValue();
|
||||
DataFormatModel model = provider.getDataFormatModel(entry.getKey());
|
||||
if (model.validateBytesPerLine(bytesPerLine)) {
|
||||
checkBox.setForeground(Color.BLACK);
|
||||
checkBox.setForeground(Colors.FOREGROUND);
|
||||
}
|
||||
else {
|
||||
checkBox.setForeground(Color.RED);
|
||||
checkBox.setForeground(Messages.ERROR);
|
||||
isBad |= checkBox.isSelected();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,6 @@ import java.util.List;
|
|||
import javax.swing.*;
|
||||
import javax.swing.event.*;
|
||||
|
||||
import docking.help.HelpService;
|
||||
import docking.widgets.fieldpanel.*;
|
||||
import docking.widgets.fieldpanel.field.EmptyTextField;
|
||||
import docking.widgets.fieldpanel.field.Field;
|
||||
|
@ -43,6 +42,7 @@ import ghidra.util.exception.InvalidInputException;
|
|||
import ghidra.util.layout.HorizontalLayout;
|
||||
import ghidra.util.layout.PairLayout;
|
||||
import help.Help;
|
||||
import help.HelpService;
|
||||
|
||||
/**
|
||||
* Top level component that contains has a scrolled pane for the panel of components that show the
|
||||
|
|
|
@ -34,6 +34,7 @@ import docking.action.DockingActionIf;
|
|||
import docking.action.ToggleDockingAction;
|
||||
import docking.widgets.EventTrigger;
|
||||
import docking.widgets.fieldpanel.support.*;
|
||||
import generic.theme.GThemeDefaults.Colors.Palette;
|
||||
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
||||
import ghidra.app.plugin.core.format.*;
|
||||
import ghidra.app.plugin.core.navigation.*;
|
||||
|
@ -1215,10 +1216,10 @@ public class ByteViewerPlugin2Test extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
Options opt = tool.getOptions("ByteViewer");
|
||||
// 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();
|
||||
assertEquals(Color.GREEN, c.getFocusedCursorColor());
|
||||
assertEquals(Palette.GREEN, c.getFocusedCursorColor());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -1235,10 +1236,10 @@ public class ByteViewerPlugin2Test extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
Options opt = tool.getOptions("ByteViewer");
|
||||
// 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");
|
||||
assertEquals(Color.GREEN, c.getNonFocusCursorColor());
|
||||
assertEquals(Palette.GREEN, c.getNonFocusCursorColor());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -1254,10 +1255,10 @@ public class ByteViewerPlugin2Test extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
Options opt = tool.getOptions("ByteViewer");
|
||||
// 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");
|
||||
assertEquals(Color.CYAN, c.getNonFocusCursorColor());
|
||||
assertEquals(Palette.CYAN, c.getNonFocusCursorColor());
|
||||
}
|
||||
|
||||
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");
|
||||
// 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));
|
||||
SwingUtilities.invokeAndWait(() -> {
|
||||
|
@ -1302,7 +1303,7 @@ public class ByteViewerPlugin2Test extends AbstractGhidraHeadedIntegrationTest {
|
|||
program.flushEvents();
|
||||
ByteViewerComponent c = panel.getCurrentComponent();
|
||||
ByteField field = c.getField(loc.getIndex(), loc.getFieldNum());
|
||||
assertEquals(Color.GREEN, field.getForeground());
|
||||
assertEquals(Palette.GREEN, field.getForeground());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -1340,12 +1341,12 @@ public class ByteViewerPlugin2Test extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
Options opt = tool.getOptions("ByteViewer");
|
||||
// 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();
|
||||
FieldLocation loc = getFieldLocation(getAddr(0x0f001000));
|
||||
ByteField field = c.getField(loc.getIndex().subtract(BigInteger.ONE), 0);
|
||||
assertEquals(Color.GREEN, field.getForeground());
|
||||
assertEquals(Palette.GREEN, field.getForeground());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
[Defaults]
|
||||
|
||||
color.bg.decompiler = color.bg
|
||||
color.fg.decompiler = color.fg
|
||||
|
||||
color.fg.decompiler.keyword = #0001e6
|
||||
color.fg.decompiler.function.name = blue
|
||||
color.fg.decompiler.comment = blueViolet
|
||||
color.fg.decompiler.error = crimson
|
||||
color.fg.decompiler.variable = #999900 // close to oliveDrab
|
||||
color.fg.decompiler.constant = forestGreen
|
||||
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.function.name = cadetBlue
|
||||
color.fg.decompiler.comment = lightSlateGray
|
||||
color.fg.decompiler.error = crimson
|
||||
color.fg.decompiler.variable = #999900 // close to oliveDrab
|
||||
color.fg.decompiler.constant = forestGreen
|
||||
color.fg.decompiler.type = blue
|
||||
|
|
|
@ -354,7 +354,7 @@ public class DecompileOptions {
|
|||
private Color defaultSearchHighlightColor = SEARCH_HIGHLIGHT_DEF;
|
||||
|
||||
// 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 Font DEFAULT_FONT = new Font(Font.MONOSPACED, Font.PLAIN, 12);
|
||||
|
|
|
@ -436,7 +436,7 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest {
|
|||
// The coloring algorithm:
|
||||
// 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,
|
||||
// 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
|
||||
//
|
||||
// This test is for 1)
|
||||
|
@ -472,7 +472,7 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest {
|
|||
// The coloring algorithm:
|
||||
// 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,
|
||||
// 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
|
||||
//
|
||||
// This test is for 2)
|
||||
|
@ -514,7 +514,7 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest {
|
|||
// The coloring algorithm:
|
||||
// 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,
|
||||
// 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
|
||||
//
|
||||
// This test is for 3)
|
||||
|
|
|
@ -29,8 +29,8 @@ import javax.swing.border.LineBorder;
|
|||
import docking.widgets.EmptyBorderButton;
|
||||
import docking.widgets.label.GDLabel;
|
||||
import generic.theme.GColor;
|
||||
import generic.theme.Gui;
|
||||
import generic.theme.GThemeDefaults.Colors.Palette;
|
||||
import generic.theme.Gui;
|
||||
import ghidra.graph.viewer.vertex.AbstractVisualVertex;
|
||||
import ghidra.graph.viewer.vertex.VertexShapeProvider;
|
||||
import ghidra.program.model.address.Address;
|
||||
|
@ -250,7 +250,7 @@ public class FcgVertex extends AbstractVisualVertex implements VertexShapeProvid
|
|||
compactShape = (Double) vertexShape.clone();
|
||||
vertexImageLabel.setIcon(new ImageIcon(image));
|
||||
|
||||
Border border = createDebugBorder(new LineBorder(Color.PINK, 1));
|
||||
Border border = createDebugBorder(new LineBorder(Palette.PINK, 1));
|
||||
vertexImageLabel.setBorder(border);
|
||||
}
|
||||
|
||||
|
@ -299,7 +299,7 @@ public class FcgVertex extends AbstractVisualVertex implements VertexShapeProvid
|
|||
|
||||
private void addNameLabel() {
|
||||
|
||||
Border border = createDebugBorder(new LineBorder(Color.GREEN, 1));
|
||||
Border border = createDebugBorder(new LineBorder(Palette.GREEN, 1));
|
||||
nameLabel.setBorder(border);
|
||||
|
||||
// assume the vertex label has been bounded
|
||||
|
|
|
@ -116,7 +116,7 @@ public class FunctionCallGraphPluginScreenShots extends GhidraScreenShotGenerato
|
|||
area.width += (2 * offset);
|
||||
area.height += (2 * offset);
|
||||
|
||||
// drawRectangle(Color.ORANGE, area, 5);
|
||||
// drawRectangle(Palette.ORANGE, area, 5);
|
||||
|
||||
crop(area);
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ import javax.swing.event.EventListenerList;
|
|||
import com.google.common.collect.HashMultiset;
|
||||
import com.google.common.collect.Multiset;
|
||||
|
||||
import generic.theme.GThemeDefaults.Colors;
|
||||
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
|
||||
*/
|
||||
private Function<String, Paint> paintFunction = v -> Color.black;
|
||||
|
||||
private Function<String, Paint> paintFunction = v -> Colors.FOREGROUND;
|
||||
|
||||
/**
|
||||
* @param excluded ignored keys
|
||||
|
|
|
@ -53,6 +53,7 @@ import docking.options.editor.OptionsDialog;
|
|||
import docking.widgets.EventTrigger;
|
||||
import docking.widgets.OptionDialog;
|
||||
import generic.theme.GColor;
|
||||
import generic.theme.GThemeDefaults.Colors;
|
||||
import generic.util.WindowUtilities;
|
||||
import ghidra.framework.options.Options;
|
||||
import ghidra.framework.options.ToolOptions;
|
||||
|
@ -1025,7 +1026,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
|||
.elements(vertices)
|
||||
.maxFactor(.05)
|
||||
.buttonSupplier(JRadioButton::new)
|
||||
.paintFunction(v -> Color.BLACK)
|
||||
.paintFunction(v -> Colors.FOREGROUND)
|
||||
.build();
|
||||
|
||||
vertexFilters.addItemListener(item -> {
|
||||
|
@ -1043,7 +1044,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
|||
.elements(edges)
|
||||
.maxFactor(.01)
|
||||
.buttonSupplier(JRadioButton::new)
|
||||
.paintFunction(e -> Color.BLACK)
|
||||
.paintFunction(e -> Colors.FOREGROUND)
|
||||
.build();
|
||||
|
||||
edgeFilters.addItemListener(item -> {
|
||||
|
|
|
@ -37,6 +37,7 @@ import org.jungrapht.visualization.renderers.Renderer;
|
|||
import org.jungrapht.visualization.renderers.Renderer.VertexLabel.Position;
|
||||
import org.jungrapht.visualization.util.RectangleUtils;
|
||||
|
||||
import generic.theme.GThemeDefaults.Colors;
|
||||
import generic.util.image.ImageUtils;
|
||||
import ghidra.service.graph.*;
|
||||
import ghidra.util.HTMLUtilities;
|
||||
|
@ -78,8 +79,8 @@ public class DefaultGraphRenderer implements GraphRenderer {
|
|||
this.options = options;
|
||||
renderingHints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
label = new JLabel();
|
||||
label.setForeground(Color.black);
|
||||
label.setBackground(Color.white);
|
||||
label.setForeground(Colors.FOREGROUND);
|
||||
label.setBackground(Colors.BACKGROUND);
|
||||
label.setOpaque(false);
|
||||
Border marginBorder = BorderFactory.createEmptyBorder(labelBorderSize, 2 * labelBorderSize,
|
||||
labelBorderSize, 2 * labelBorderSize);
|
||||
|
@ -163,7 +164,7 @@ public class DefaultGraphRenderer implements GraphRenderer {
|
|||
}
|
||||
|
||||
renderContext.setVertexFontFunction(this::getFont);
|
||||
renderContext.setVertexLabelRenderer(new JLabelVertexLabelRenderer(Color.black));
|
||||
renderContext.setVertexLabelRenderer(new JLabelVertexLabelRenderer(Colors.FOREGROUND));
|
||||
renderContext.setVertexDrawPaintFunction(this::getVertexColor);
|
||||
renderContext.setVertexFillPaintFunction(this::getVertexColor);
|
||||
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
|
||||
graphics.translate(-bounds.x + strokeThickness, -bounds.y + strokeThickness);
|
||||
graphics.setPaint(Color.WHITE);
|
||||
graphics.setPaint(Colors.BACKGROUND);
|
||||
graphics.fill(scaledShape);
|
||||
graphics.setPaint(vertexColor);
|
||||
graphics.setStroke(new BasicStroke(strokeThickness));
|
||||
|
@ -304,7 +305,7 @@ public class DefaultGraphRenderer implements GraphRenderer {
|
|||
int yOffset = (int) ((iconHeight - label.getHeight()) * labelOffsetRatio);
|
||||
|
||||
graphics.translate(xOffset, yOffset);
|
||||
graphics.setPaint(Color.black);
|
||||
graphics.setPaint(Colors.FOREGROUND);
|
||||
label.paint(graphics);
|
||||
|
||||
graphics.setTransform(graphicsTransform); // restore the original transform
|
||||
|
|
|
@ -42,6 +42,7 @@ import docking.widgets.label.GLabel;
|
|||
import docking.widgets.textfield.HexOrDecimalInput;
|
||||
import docking.widgets.textfield.HintTextField;
|
||||
import generic.theme.GThemeDefaults.Colors;
|
||||
import generic.theme.GThemeDefaults.Colors.Messages;
|
||||
import generic.theme.TempColorUtils;
|
||||
import ghidra.app.util.bin.format.pdb.PdbParser;
|
||||
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbIdentifiers;
|
||||
|
@ -910,7 +911,7 @@ public class LoadPdbDialog extends DialogComponentProvider {
|
|||
}
|
||||
|
||||
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);
|
||||
|
||||
Dimension size = getSize();
|
||||
|
|
|
@ -26,7 +26,6 @@ import javax.swing.text.*;
|
|||
import javax.swing.tree.TreeSelectionModel;
|
||||
|
||||
import docking.DockingUtils;
|
||||
import docking.help.HelpService;
|
||||
import docking.widgets.EventTrigger;
|
||||
import docking.widgets.OptionDialog;
|
||||
import docking.widgets.fieldpanel.FieldPanel;
|
||||
|
@ -63,6 +62,7 @@ import ghidra.util.exception.CancelledException;
|
|||
import ghidra.util.exception.VersionException;
|
||||
import ghidra.util.task.*;
|
||||
import help.Help;
|
||||
import help.HelpService;
|
||||
import resources.ResourceManager;
|
||||
|
||||
/**
|
||||
|
|
|
@ -33,6 +33,7 @@ import docking.action.ToggleDockingAction;
|
|||
import docking.tool.ToolConstants;
|
||||
import docking.widgets.fieldpanel.FieldPanel;
|
||||
import generic.test.AbstractGenericTest;
|
||||
import generic.theme.GThemeDefaults.Colors.Palette;
|
||||
import ghidra.app.events.ProgramLocationPluginEvent;
|
||||
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
||||
import ghidra.app.plugin.core.marker.MarkerManagerPlugin;
|
||||
|
@ -271,10 +272,10 @@ public class DiffTestAdapter extends AbstractGhidraHeadedIntegrationTest {
|
|||
builder.setIntProperty("10018ff", "Space", 1);
|
||||
builder.setIntProperty("100248c", "Space", 1);
|
||||
|
||||
builder.setObjectProperty("100248c", "testColor", new SaveableColor(Color.CYAN));
|
||||
builder.setObjectProperty("10039dd", "testColor", new SaveableColor(Color.BLACK));
|
||||
builder.setObjectProperty("10039f8", "testColor", new SaveableColor(Color.BLACK));
|
||||
builder.setObjectProperty("10039fe", "testColor", new SaveableColor(Color.RED));
|
||||
builder.setObjectProperty("100248c", "testColor", new SaveableColor(Palette.CYAN));
|
||||
builder.setObjectProperty("10039dd", "testColor", new SaveableColor(Palette.BLACK));
|
||||
builder.setObjectProperty("10039f8", "testColor", new SaveableColor(Palette.BLACK));
|
||||
builder.setObjectProperty("10039fe", "testColor", new SaveableColor(Palette.RED));
|
||||
|
||||
AbstractGenericTest.setInstanceField("recordChanges", builder.getProgram(), Boolean.TRUE);
|
||||
|
||||
|
@ -450,10 +451,10 @@ public class DiffTestAdapter extends AbstractGhidraHeadedIntegrationTest {
|
|||
builder.setIntProperty("1002428", "Space", 1);
|
||||
builder.setIntProperty("100248c", "Space", 1);
|
||||
|
||||
builder.setObjectProperty("100248c", "testColor", new SaveableColor(Color.WHITE));
|
||||
builder.setObjectProperty("10039f1", "testColor", new SaveableColor(Color.BLACK));
|
||||
builder.setObjectProperty("10039f8", "testColor", new SaveableColor(Color.BLACK));
|
||||
builder.setObjectProperty("10039fe", "testColor", new SaveableColor(Color.GREEN));
|
||||
builder.setObjectProperty("100248c", "testColor", new SaveableColor(Palette.WHITE));
|
||||
builder.setObjectProperty("10039f1", "testColor", new SaveableColor(Palette.BLACK));
|
||||
builder.setObjectProperty("10039f8", "testColor", new SaveableColor(Palette.BLACK));
|
||||
builder.setObjectProperty("10039fe", "testColor", new SaveableColor(Palette.GREEN));
|
||||
|
||||
AbstractGenericTest.setInstanceField("recordChanges", builder.getProgram(), Boolean.TRUE);
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
##MODULE IP: Jython License
|
||||
##MODULE IP: LGPL 2.1
|
||||
Module.manifest||GHIDRA||||END|
|
||||
data/python.theme.properties||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/interpreter.html||GHIDRA||||END|
|
||||
|
|
19
Ghidra/Features/Python/data/python.theme.properties
Normal 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]
|
||||
|
|
@ -25,6 +25,7 @@ import org.python.core.PyInstance;
|
|||
import org.python.core.PyObject;
|
||||
|
||||
import docking.widgets.label.GDLabel;
|
||||
import generic.theme.GColor;
|
||||
import ghidra.app.plugin.core.console.CodeCompletion;
|
||||
import ghidra.framework.options.Options;
|
||||
import ghidra.util.Msg;
|
||||
|
@ -57,19 +58,21 @@ public class PythonCodeCompletionFactory {
|
|||
private final static boolean INCLUDE_TYPES_DEFAULT = true;
|
||||
private static boolean includeTypes = INCLUDE_TYPES_DEFAULT;
|
||||
|
||||
public static final Color NULL_COLOR = new Color(255, 0, 0);
|
||||
public static final Color FUNCTION_COLOR = new Color(0, 128, 0);
|
||||
public static final Color PACKAGE_COLOR = new Color(128, 0, 0);
|
||||
public static final Color CLASS_COLOR = new Color(0, 0, 255);
|
||||
public static final Color METHOD_COLOR = new Color(0, 128, 128);
|
||||
//@formatter:off
|
||||
public static final Color NULL_COLOR = new GColor("color.fg.plugin.python.syntax.null");
|
||||
public static final Color FUNCTION_COLOR = new GColor("color.fg.plugin.python.syntax.function");
|
||||
public static final Color PACKAGE_COLOR = new GColor("color.fg.plugin.python.syntax.package");
|
||||
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 */
|
||||
public static final Color CODE_COLOR = new Color(0, 64, 0);
|
||||
public static final Color INSTANCE_COLOR = new Color(128, 0, 128);
|
||||
public static final Color SEQUENCE_COLOR = new Color(128, 96, 64);
|
||||
public static final Color MAP_COLOR = new Color(64, 96, 128);
|
||||
public static final Color NUMBER_COLOR = new Color(64, 64, 64);
|
||||
public static final Color CODE_COLOR = new GColor("color.fg.plugin.python.syntax.code");
|
||||
public static final Color INSTANCE_COLOR = new GColor("color.fg.plugin.python.syntax.instance");
|
||||
public static final Color SEQUENCE_COLOR = new GColor("color.fg.plugin.python.syntax.sequence");
|
||||
public static final Color MAP_COLOR = new GColor("color.fg.plugin.python.syntax.map");
|
||||
public static final Color NUMBER_COLOR = new GColor("color.fg.plugin.python.syntax.number");
|
||||
/* 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 {
|
||||
/* Order matters! This is the order in which classes are checked for
|
||||
|
|
|
@ -9,21 +9,14 @@
|
|||
##MODULE IP: Tango Icons - Public Domain
|
||||
Module.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.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/options/editor/package.html||GHIDRA||reviewed||END|
|
||||
src/main/java/docking/widgets/fieldpanel/package.html||GHIDRA||reviewed||END|
|
||||
|
|
|
@ -6,17 +6,21 @@ color.palette.disabled = rgba(255,255,255,0)
|
|||
color.palette.black = black
|
||||
color.palette.blue = blue
|
||||
color.palate.cornflowerblue = cornflowerblue
|
||||
color.palate.cornsilk = cornsilk
|
||||
color.palette.crimson = crimson
|
||||
color.palette.cyan = cyan
|
||||
color.palette.darkblue = DarkBlue
|
||||
color.palette.darkgray = DarkGray
|
||||
color.palette.darkgreen = darkgreen
|
||||
color.palette.darkkhaki = DarkKhaki
|
||||
color.palette.darkred = DarkRed
|
||||
color.palette.darkslategray = darkslategray
|
||||
color.palette.dodgerblue = DodgerBlue
|
||||
color.palette.fuchsia = fuchsia
|
||||
color.palette.gold = gold
|
||||
color.palette.gray = gray
|
||||
color.palette.green = green
|
||||
color.palette.greenyellow = greenyellow
|
||||
color.palette.indigo = indigo
|
||||
color.palette.khaki = khaki
|
||||
color.palette.lavender = lavender
|
||||
|
@ -36,10 +40,14 @@ color.palette.olive = olive
|
|||
color.palette.orange = orange
|
||||
color.palette.palegreen = palegreen
|
||||
color.palette.palevioletred = PaleVioletRed
|
||||
color.palette.peachpuff = peachpuff
|
||||
color.palette.pink = pink
|
||||
color.palette.purple = purple
|
||||
color.palette.red = red
|
||||
color.palette.silver = silver
|
||||
color.palette.steelblue = steelblue
|
||||
color.palette.tan = tan
|
||||
color.palette.teal = teal
|
||||
color.palette.yellow = yellow
|
||||
color.palette.yellowgreen = yellowgreen
|
||||
color.palette.white = white
|
||||
|
|
|
@ -73,7 +73,7 @@ color.bg.tree.drag.no.selection = rgb(204, 204, 255)
|
|||
color.bg.filterfield = color.bg.filtered
|
||||
color.fg.filterfield = black
|
||||
|
||||
color.bg.selection.help = lightSteelBlue
|
||||
color.bg.selection.help = lightsteelblue
|
||||
|
||||
// generic component items
|
||||
color.border.bevel.highlight = lightGray
|
||||
|
|
|
@ -20,9 +20,9 @@
|
|||
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
|
||||
<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
|
||||
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.
|
||||
|
||||
|
||||
|
@ -49,6 +49,8 @@
|
|||
|
||||
|
||||
<tocroot>
|
||||
|
||||
|
||||
<tocdef id="Theming"
|
||||
text="Theming"
|
||||
target="help/topics/Theming/Theming.htm" >
|
||||
</tocdef>
|
||||
</tocroot>
|
||||
|
|
Before Width: | Height: | Size: 69 B |
Before Width: | Height: | Size: 859 B |
Before Width: | Height: | Size: 62 B |
Before Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 187 B |
Before Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 185 B |
Before Width: | Height: | Size: 1.3 KiB |
|
@ -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>
|
|
@ -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 <application dir>/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>
|
||||
</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>
|
||||
</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>
|
||||
</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>
|
||||
</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><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>
|
After Width: | Height: | Size: 25 KiB |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 6.7 KiB |
After Width: | Height: | Size: 46 KiB |
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -37,6 +37,7 @@ 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;
|
||||
|
@ -398,7 +399,7 @@ public class DockingHelpBroker extends GHelpBroker {
|
|||
|
||||
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;
|
||||
|
||||
LocationHintPainter(Shape paintShape) {
|
||||
|
@ -450,11 +451,11 @@ public class DockingHelpBroker extends GHelpBroker {
|
|||
/*
|
||||
// Debug
|
||||
Shape box = scaler.createTransformedShape(b);
|
||||
g2d.setColor(Color.GREEN);
|
||||
g2d.setColor(Palette.GREEN);
|
||||
g2d.fill(box);
|
||||
|
||||
box = transform.createTransformedShape(box);
|
||||
g2d.setColor(Color.YELLOW);
|
||||
g2d.setColor(Palette.YELLOW);
|
||||
g2d.fill(box);
|
||||
*/
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -67,6 +67,13 @@ public class ThemeColorTableModel extends GDynamicColumnTableModel<ColorValue, O
|
|||
fireTableDataChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the original value for the current theme
|
||||
*/
|
||||
public ColorValue getThemeValue(String id) {
|
||||
return themeValues.getColor(id);
|
||||
}
|
||||
|
||||
private void load() {
|
||||
currentValues = Gui.getAllValues();
|
||||
colors = currentValues.getColors();
|
||||
|
@ -203,11 +210,11 @@ public class ThemeColorTableModel extends GDynamicColumnTableModel<ColorValue, O
|
|||
return "<No Value>";
|
||||
}
|
||||
Color color = resolvedColor.color();
|
||||
String text = WebColors.toString(color, false);
|
||||
String name = WebColors.toWebColorName(color);
|
||||
if (resolvedColor.refId() != null) {
|
||||
return "[" + resolvedColor.refId() + "] " + text;
|
||||
return "[" + resolvedColor.refId() + "] ";
|
||||
}
|
||||
String text = WebColors.toString(color, false);
|
||||
if (name != null) {
|
||||
text += " (" + name + ")";
|
||||
}
|
||||
|
@ -250,4 +257,5 @@ public class ThemeColorTableModel extends GDynamicColumnTableModel<ColorValue, O
|
|||
}
|
||||
|
||||
record ResolvedColor(String id, String refId, Color color) {/**/}
|
||||
|
||||
}
|
||||
|
|
|
@ -18,43 +18,36 @@ package docking.theme.gui;
|
|||
import java.awt.BorderLayout;
|
||||
import java.awt.Component;
|
||||
import java.awt.event.*;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.table.TableColumn;
|
||||
|
||||
import docking.DialogComponentProvider;
|
||||
import docking.DockingWindowManager;
|
||||
import docking.*;
|
||||
import docking.action.ActionContextProvider;
|
||||
import docking.action.DockingAction;
|
||||
import docking.action.builder.ActionBuilder;
|
||||
import docking.widgets.OptionDialog;
|
||||
import docking.widgets.combobox.GhidraComboBox;
|
||||
import docking.widgets.table.GFilterTable;
|
||||
import docking.widgets.table.GTable;
|
||||
import generic.theme.*;
|
||||
import ghidra.util.MessageType;
|
||||
import ghidra.util.Swing;
|
||||
import ghidra.util.*;
|
||||
|
||||
/**
|
||||
* Primary dialog for editing Themes.
|
||||
*/
|
||||
public class ThemeDialog extends DialogComponentProvider {
|
||||
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 restoreButton;
|
||||
private GhidraComboBox<String> combo;
|
||||
private ItemListener comboListener = this::themeComboChanged;
|
||||
private ThemeListener listener = new DialogThemeListener();
|
||||
private JTabbedPane tabbedPane;
|
||||
|
||||
private ThemeColorTable colorTable;
|
||||
private ThemeFontTable fontTable;
|
||||
private ThemeIconTable iconTable;
|
||||
|
||||
public ThemeDialog() {
|
||||
super("Theme Dialog", false);
|
||||
|
@ -69,14 +62,26 @@ public class ThemeDialog extends DialogComponentProvider {
|
|||
updateButtons();
|
||||
createActions();
|
||||
Gui.addThemeListener(listener);
|
||||
setHelpLocation(new HelpLocation("Theming", "Edit_Theme"));
|
||||
}
|
||||
|
||||
private void createActions() {
|
||||
DockingAction reloadDefaultsAction = new ActionBuilder("Reload Ghidra Defaults", getTitle())
|
||||
.toolBarIcon(new GIcon("icon.refresh"))
|
||||
.helpLocation(new HelpLocation("Theming", "Reload_Ghidra_Defaults"))
|
||||
.onAction(e -> reloadDefaultsCallback())
|
||||
.build();
|
||||
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
|
||||
|
@ -129,9 +134,9 @@ public class ThemeDialog extends DialogComponentProvider {
|
|||
}
|
||||
|
||||
private void reset() {
|
||||
colorTableModel.reloadAll();
|
||||
fontTableModel.reloadAll();
|
||||
iconTableModel.reloadAll();
|
||||
colorTable.reloadAll();
|
||||
fontTable.reloadAll();
|
||||
iconTable.reloadAll();
|
||||
updateButtons();
|
||||
updateCombo();
|
||||
}
|
||||
|
@ -159,45 +164,9 @@ public class ThemeDialog extends DialogComponentProvider {
|
|||
else {
|
||||
setStatusText("");
|
||||
}
|
||||
colorTableModel.reloadAll();
|
||||
fontTableModel.reloadAll();
|
||||
iconTableModel.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);
|
||||
colorTable.reloadAll();
|
||||
fontTable.reloadAll();
|
||||
iconTable.reloadAll();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -253,130 +222,16 @@ public class ThemeDialog extends DialogComponentProvider {
|
|||
}
|
||||
|
||||
private Component buildTabedTables() {
|
||||
JTabbedPane tabbedPane = new JTabbedPane();
|
||||
tabbedPane.add("Colors", buildColorTable());
|
||||
tabbedPane.add("Fonts", buildFontTable());
|
||||
tabbedPane.add("Icons", buildIconTable());
|
||||
tabbedPane = new JTabbedPane();
|
||||
colorTable = new ThemeColorTable();
|
||||
fontTable = new ThemeFontTable();
|
||||
iconTable = new ThemeIconTable();
|
||||
tabbedPane.add("Colors", colorTable);
|
||||
tabbedPane.add("Fonts", fontTable);
|
||||
tabbedPane.add("Icons", iconTable);
|
||||
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() {
|
||||
restoreButton = new JButton("Restore");
|
||||
restoreButton.setMnemonic('R');
|
||||
|
@ -411,6 +266,13 @@ public class ThemeDialog extends DialogComponentProvider {
|
|||
super.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionContext getActionContext(MouseEvent event) {
|
||||
ActionContextProvider contextProvider =
|
||||
(ActionContextProvider) tabbedPane.getSelectedComponent();
|
||||
return contextProvider.getActionContext(event);
|
||||
}
|
||||
|
||||
class DialogThemeListener implements ThemeListener {
|
||||
@Override
|
||||
public void themeChanged(ThemeEvent event) {
|
||||
|
@ -419,13 +281,13 @@ public class ThemeDialog extends DialogComponentProvider {
|
|||
return;
|
||||
}
|
||||
if (event.hasAnyColorChanged()) {
|
||||
colorTableModel.reloadCurrent();
|
||||
colorTable.reloadCurrent();
|
||||
}
|
||||
if (event.hasAnyFontChanged()) {
|
||||
fontTableModel.reloadCurrent();
|
||||
fontTable.reloadCurrent();
|
||||
}
|
||||
if (event.hasAnyIconChanged()) {
|
||||
iconTableModel.reloadCurrent();
|
||||
iconTable.reloadCurrent();
|
||||
}
|
||||
updateButtons();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -100,7 +100,7 @@ public class ThemeFontTableModel extends GDynamicColumnTableModel<FontValue, Obj
|
|||
return "<No Value>";
|
||||
}
|
||||
if (fontValue.getReferenceId() != null) {
|
||||
return fontValue.getReferenceId();
|
||||
return "[" + fontValue.getReferenceId() + "]";
|
||||
}
|
||||
|
||||
Font font = fontValue.getRawValue();
|
||||
|
@ -178,8 +178,6 @@ public class ThemeFontTableModel extends GDynamicColumnTableModel<FontValue, Obj
|
|||
}
|
||||
|
||||
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
|
||||
public Component getTableCellRendererComponent(GTableCellRenderingData data) {
|
||||
|
@ -187,12 +185,6 @@ public class ThemeFontTableModel extends GDynamicColumnTableModel<FontValue, Obj
|
|||
FontValue fontValue = (FontValue) data.getValue();
|
||||
|
||||
String text = getValueText(fontValue);
|
||||
if (fontValue.getReferenceId() != null) {
|
||||
label.setFont(indirectFont);
|
||||
}
|
||||
else {
|
||||
label.setFont(regularFont);
|
||||
}
|
||||
label.setText(text);
|
||||
label.setOpaque(true);
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -229,8 +229,16 @@ public class ThemeIconTableModel extends GDynamicColumnTableModel<IconValue, Obj
|
|||
public String getFilterString(ResolvedIcon iconValue, Settings settings) {
|
||||
return getValueText(iconValue);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -365,7 +365,7 @@ public class ConditionTestPanel extends JPanel {
|
|||
public TestPanel(ConditionTester conditionTest) {
|
||||
super(new PairLayout());
|
||||
backgroundColor = getBackground();
|
||||
selectedColor = Color.LIGHT_GRAY;
|
||||
selectedColor = Palette.LIGHT_GRAY;
|
||||
this.test = conditionTest;
|
||||
checkbox = new GCheckBox();
|
||||
checkbox.setSelected(true);
|
||||
|
|
|
@ -25,6 +25,7 @@ import docking.DialogComponentProvider;
|
|||
import docking.DockingUtils;
|
||||
import docking.widgets.label.GDLabel;
|
||||
import docking.widgets.label.GLabel;
|
||||
import generic.theme.GThemeDefaults.Colors.Messages;
|
||||
import ghidra.framework.OperatingSystem;
|
||||
import ghidra.framework.Platform;
|
||||
|
||||
|
@ -87,7 +88,7 @@ public class MultiLineInputDialog extends DialogComponentProvider {
|
|||
Font smallerFont = font.deriveFont(12F);
|
||||
Font smallItalicFont = smallerFont.deriveFont(Font.ITALIC);
|
||||
hintLabel.setFont(smallItalicFont);
|
||||
hintLabel.setForeground(Color.LIGHT_GRAY);
|
||||
hintLabel.setForeground(Messages.HINT);
|
||||
|
||||
dataPanel.add(messageLabel, BorderLayout.NORTH);
|
||||
dataPanel.add(new JScrollPane(inputTextArea), BorderLayout.CENTER);
|
||||
|
|
|
@ -23,6 +23,7 @@ import javax.swing.JTextField;
|
|||
import javax.swing.text.*;
|
||||
|
||||
import docking.util.GraphicsUtils;
|
||||
import generic.theme.GThemeDefaults.Colors.Messages;
|
||||
|
||||
public class HexOrDecimalInput extends JTextField {
|
||||
private boolean isHexMode = false;
|
||||
|
@ -130,7 +131,7 @@ public class HexOrDecimalInput extends JTextField {
|
|||
Font font = new Font("Monospaced", Font.PLAIN, 10);
|
||||
Font savedFont = g.getFont();
|
||||
g.setFont(font);
|
||||
g.setColor(Color.LIGHT_GRAY);
|
||||
g.setColor(Messages.HINT);
|
||||
FontMetrics fontMetrics = getFontMetrics(font);
|
||||
String mode = isHexMode ? "Hex" : "Dec";
|
||||
int stringWidth = fontMetrics.stringWidth(mode);
|
||||
|
|
|
@ -15,35 +15,19 @@
|
|||
*/
|
||||
package docking.widgets.textfield;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
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.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.ToolTipManager;
|
||||
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 javax.swing.*;
|
||||
import javax.swing.event.*;
|
||||
import javax.swing.text.*;
|
||||
|
||||
import docking.DockingUtils;
|
||||
import docking.util.GraphicsUtils;
|
||||
import generic.theme.GThemeDefaults.Colors.Messages;
|
||||
import ghidra.util.SystemUtilities;
|
||||
|
||||
/**
|
||||
|
@ -699,7 +683,7 @@ public class IntegerTextField {
|
|||
|
||||
Font savedFont = g.getFont();
|
||||
g.setFont(hintFont);
|
||||
g.setColor(Color.LIGHT_GRAY);
|
||||
g.setColor(Messages.HINT);
|
||||
|
||||
Dimension size = getSize();
|
||||
Insets insets = getInsets();
|
||||
|
|
|
@ -31,6 +31,7 @@ import javax.swing.JTextField;
|
|||
import javax.swing.event.*;
|
||||
|
||||
import docking.util.GraphicsUtils;
|
||||
import generic.theme.GThemeDefaults.Colors.Messages;
|
||||
import ghidra.util.SystemUtilities;
|
||||
|
||||
/**
|
||||
|
@ -234,7 +235,7 @@ public class LocalDateTextField {
|
|||
Font font = new Font("Monospaced", Font.PLAIN, 10);
|
||||
Font savedFont = g.getFont();
|
||||
g.setFont(font);
|
||||
g.setColor(Color.LIGHT_GRAY);
|
||||
g.setColor(Messages.HINT);
|
||||
FontMetrics fontMetrics = getFontMetrics(font);
|
||||
String label = isMonthMode ? MONTH_LABEL : DAY_LABEL;
|
||||
int stringWidth = fontMetrics.stringWidth(label);
|
||||
|
|
|
@ -32,12 +32,13 @@ import docking.widgets.OptionDialog;
|
|||
import docking.widgets.SelectFromListDialog;
|
||||
import docking.widgets.dialogs.InputDialog;
|
||||
import generic.theme.*;
|
||||
import generic.theme.GThemeDefaults.Colors.Palette;
|
||||
import generic.theme.builtin.MetalTheme;
|
||||
import generic.theme.builtin.NimbusTheme;
|
||||
|
||||
public class ThemeUtilsTest extends AbstractDockingTest {
|
||||
|
||||
private Color testColor = Color.RED;
|
||||
private Color testColor = Palette.RED;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
|
|
|
@ -149,4 +149,9 @@ public class ColorValue extends ThemeValue<Color> {
|
|||
return outputString;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void makeCurrentValue() {
|
||||
Gui.setColor(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -223,4 +223,9 @@ public class FontValue extends ThemeValue<Font> {
|
|||
return "PLAIN";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void makeCurrentValue() {
|
||||
Gui.setFont(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -146,4 +146,9 @@ public class IconValue extends ThemeValue<Icon> {
|
|||
return iconToString(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void makeCurrentValue() {
|
||||
Gui.setIcon(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -202,4 +202,9 @@ public abstract class ThemeValue<T> implements Comparable<ThemeValue<T>> {
|
|||
return name + " (" + id + ", " + referenceId + ")";
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes this value the current value for the application
|
||||
*/
|
||||
public abstract void makeCurrentValue();
|
||||
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ package generic.theme.laf;
|
|||
|
||||
import java.awt.Component;
|
||||
import java.awt.Font;
|
||||
import java.util.Objects;
|
||||
|
||||
import generic.theme.Gui;
|
||||
import ghidra.util.datastruct.WeakDataStructureFactory;
|
||||
|
@ -54,7 +55,10 @@ public class ComponentFontRegistry {
|
|||
public void updateComponentFonts() {
|
||||
Font font = Gui.getFont(fontId);
|
||||
for (Component component : components) {
|
||||
Font existingFont = component.getFont();
|
||||
if (!Objects.equals(existingFont, font)) {
|
||||
component.setFont(font);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -105,7 +105,7 @@ public abstract class LookAndFeelManager {
|
|||
updateComponentUis();
|
||||
}
|
||||
|
||||
private void updateAllRegisteredComponentFonts() {
|
||||
protected void updateAllRegisteredComponentFonts() {
|
||||
for (ComponentFontRegistry register : fontRegistryMap.values()) {
|
||||
register.updateComponentFonts();
|
||||
}
|
||||
|
@ -178,21 +178,12 @@ public abstract class LookAndFeelManager {
|
|||
Font font = Gui.getFont(javaFontId);
|
||||
defaults.put(javaFontId, new FontUIResource(font));
|
||||
}
|
||||
updateComponentFonts(changedJavaFontIds);
|
||||
updateComponentUis();
|
||||
}
|
||||
updateAllRegisteredComponentFonts();
|
||||
repaintAll();
|
||||
}
|
||||
|
||||
protected void updateComponentFonts(Set<String> changedFontIds) {
|
||||
for (String javaFontId : changedFontIds) {
|
||||
ComponentFontRegistry register = fontRegistryMap.get(javaFontId);
|
||||
if (register != null) {
|
||||
register.updateComponentFonts();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void updateComponentUis() {
|
||||
for (Window window : Window.getWindows()) {
|
||||
SwingUtilities.updateComponentTreeUI(window);
|
||||
|
|
|
@ -48,8 +48,8 @@ public class NimbusLookAndFeelManager extends LookAndFeelManager {
|
|||
public void fontsChanged(Set<String> affectedJavaIds) {
|
||||
if (!affectedJavaIds.isEmpty()) {
|
||||
reinstallNimubus();
|
||||
updateComponentFonts(affectedJavaIds);
|
||||
}
|
||||
updateAllRegisteredComponentFonts();
|
||||
repaintAll();
|
||||
}
|
||||
|
||||
|
|
|
@ -21,10 +21,10 @@ import java.io.IOException;
|
|||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.ImageIcon;
|
||||
|
||||
import generic.Images;
|
||||
import generic.theme.GIcon;
|
||||
import generic.util.image.ImageUtils;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
|
@ -37,18 +37,19 @@ import ghidra.util.Msg;
|
|||
*/
|
||||
public class IconProvider {
|
||||
|
||||
private GIcon icon;
|
||||
private Icon icon;
|
||||
private URL url;
|
||||
private URL tempUrl;
|
||||
private boolean tempFileFailed;
|
||||
|
||||
public IconProvider(GIcon icon, URL url) {
|
||||
public IconProvider(Icon icon, URL url) {
|
||||
this.icon = icon;
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public Image getImage() {
|
||||
return icon.getImageIcon().getImage();
|
||||
ImageIcon imageIcon = ResourceManager.getImageIcon(icon);
|
||||
return imageIcon.getImage();
|
||||
}
|
||||
|
||||
public boolean isInvalid() {
|
||||
|
@ -96,7 +97,7 @@ public class IconProvider {
|
|||
try {
|
||||
File imageFile = File.createTempFile("temp.help.icon", null);
|
||||
imageFile.deleteOnExit(); // don't let this linger
|
||||
ImageIcon imageIcon = icon.getImageIcon();
|
||||
ImageIcon imageIcon = ResourceManager.getImageIcon(icon);
|
||||
ImageUtils.writeFile(imageIcon.getImage(), imageFile);
|
||||
return imageFile.toURI().toURL();
|
||||
}
|
||||
|
|
|
@ -113,7 +113,7 @@ public class Icons {
|
|||
return null;
|
||||
}
|
||||
|
||||
GIcon icon = getIconByFieldName(fieldName);
|
||||
Icon icon = getIconByFieldName(fieldName);
|
||||
if (icon == null) {
|
||||
return null;
|
||||
}
|
||||
|
@ -165,12 +165,12 @@ public class Icons {
|
|||
return fieldName;
|
||||
}
|
||||
|
||||
private static GIcon getIconByFieldName(String fieldName) {
|
||||
private static Icon getIconByFieldName(String fieldName) {
|
||||
|
||||
try {
|
||||
Field field = Icons.class.getField(fieldName);
|
||||
Object object = field.get(Icons.class);
|
||||
GIcon icon = (GIcon) object;
|
||||
Icon icon = (Icon) object;
|
||||
return icon;
|
||||
}
|
||||
catch (Exception e) {
|
||||
|
@ -180,16 +180,14 @@ public class Icons {
|
|||
}
|
||||
}
|
||||
|
||||
private static URL getUrlFromIcon(GIcon icon) {
|
||||
if (icon == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
URL url = icon.getUrl();
|
||||
private static URL getUrlFromIcon(Icon icon) {
|
||||
if (icon instanceof GIcon gIcon) {
|
||||
URL url = gIcon.getUrl();
|
||||
if (url != null) {
|
||||
return url;
|
||||
}
|
||||
Msg.debug(Icons.class, "Unable to get URL for icon");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
@ -24,6 +24,7 @@ import java.net.URL;
|
|||
import java.util.*;
|
||||
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.plaf.UIResource;
|
||||
|
||||
import org.junit.Before;
|
||||
|
@ -343,6 +344,17 @@ public class GuiTest {
|
|||
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) {
|
||||
if (color.getRGB() != gColor.getRGB()) {
|
||||
fail("RGB values don't match! Expected " + color + " but got " + gColor);
|
||||
|
|
|
@ -27,7 +27,6 @@ import com.google.common.base.Function;
|
|||
import docking.DockingUtils;
|
||||
import docking.DockingWindowManager;
|
||||
import docking.actions.KeyBindingUtils;
|
||||
import docking.help.HelpService;
|
||||
import docking.widgets.EmptyBorderButton;
|
||||
import docking.widgets.PopupWindow;
|
||||
import docking.widgets.label.GIconLabel;
|
||||
|
|
|
@ -88,7 +88,7 @@ public class AlgorithmTestSteppingVertex<V> extends AbstractTestVertex
|
|||
|
||||
private void buildShapes() {
|
||||
|
||||
defaultShape = buildCircleShape(Color.LIGHT_GRAY, "default");
|
||||
defaultShape = buildCircleShape(Palette.LIGHT_GRAY, "default");
|
||||
defaultWithPathShape =
|
||||
buildCircleShape(new GColor("color.palette.yellowgreen"), "default; was in path");
|
||||
scheduledShape = buildCircleShape(new GColor("color.palette.khaki"), "scheduled");
|
||||
|
|
|
@ -28,7 +28,6 @@ import generic.theme.GThemeDefaults.Colors.Palette;
|
|||
import ghidra.framework.options.Options;
|
||||
import ghidra.framework.options.ToolOptions;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.WebColors;
|
||||
|
||||
public class GraphDisplayOptionsTest {
|
||||
|
||||
|
@ -154,9 +153,9 @@ public class GraphDisplayOptionsTest {
|
|||
vertex.setVertexType("V1");
|
||||
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
|
||||
|
@ -184,9 +183,9 @@ public class GraphDisplayOptionsTest {
|
|||
edge.setEdgeType("E1");
|
||||
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
|
||||
|
@ -269,7 +268,7 @@ public class GraphDisplayOptionsTest {
|
|||
|
||||
AttributedVertex vertex = new AttributedVertex("Foo");
|
||||
vertex.setVertexType("V1");
|
||||
assertEquals(Palette.BLUE, options.getVertexColor(vertex));
|
||||
assertEquals(Palette.BLUE.getRGB(), options.getVertexColor(vertex).getRGB());
|
||||
|
||||
Options graphDisplayOptions = toolOptions.getOptions(options.getRootOptionsName());
|
||||
Options vertexColorOptions = graphDisplayOptions.getOptions("Vertex Colors");
|
||||
|
|
|
@ -74,6 +74,11 @@ public class DefaultHelpService implements HelpService {
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload() {
|
||||
// no-op
|
||||
}
|
||||
|
||||
private void displayHelpInfo(Object helpObj) {
|
||||
String msg = getHelpInfo(helpObj);
|
||||
Msg.showInfo(this, null, "Help Info", msg);
|
||||
|
|
|
@ -46,6 +46,7 @@ public class GHelpBroker extends DefaultHelpBroker {
|
|||
|
||||
protected JEditorPane htmlEditorPane;
|
||||
private Window activationWindow;
|
||||
private boolean initialized;
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
// this base class does not have a navigation aid
|
||||
}
|
||||
|
@ -134,7 +151,7 @@ public class GHelpBroker extends DefaultHelpBroker {
|
|||
}
|
||||
|
||||
private void initializeScreenDevice() {
|
||||
if (isInitialized()) {
|
||||
if (initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -179,20 +196,21 @@ public class GHelpBroker extends DefaultHelpBroker {
|
|||
initializeUIComponents(contentPane);
|
||||
}
|
||||
|
||||
private boolean isInitialized() {
|
||||
return htmlEditorPane != null;
|
||||
}
|
||||
|
||||
private void initializeUIComponents(Container contentPane) {
|
||||
|
||||
if (isInitialized()) {
|
||||
if (initialized) {
|
||||
return;// already initialized
|
||||
}
|
||||
|
||||
Component[] components = contentPane.getComponents();
|
||||
JHelp jHelp = (JHelp) components[0];
|
||||
addCustomToolbarItems(jHelp);
|
||||
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
|
||||
|
@ -203,6 +221,7 @@ public class GHelpBroker extends DefaultHelpBroker {
|
|||
}
|
||||
|
||||
installActions(jHelp);
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
protected void installHelpSearcher(JHelp jHelp, HelpModel helpModel) {
|
||||
|
|
|
@ -26,7 +26,6 @@ import java.util.List;
|
|||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JEditorPane;
|
||||
import javax.swing.event.HyperlinkEvent;
|
||||
import javax.swing.event.HyperlinkListener;
|
||||
|
@ -496,8 +495,7 @@ public class GHelpHTMLEditorKit extends HTMLEditorKit {
|
|||
return null;
|
||||
}
|
||||
|
||||
ImageIcon imageIcon = iconProvider.getIcon();
|
||||
this.image = imageIcon.getImage();
|
||||
this.image = iconProvider.getImage();
|
||||
|
||||
URL url = iconProvider.getOrCreateUrl();
|
||||
return url;
|
||||
|
|
|
@ -113,4 +113,9 @@ public interface HelpService {
|
|||
* initializing
|
||||
*/
|
||||
public boolean helpExists();
|
||||
|
||||
/**
|
||||
* Called when a major system even happens, such as changing the system theme.
|
||||
*/
|
||||
public void reload();
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ apply from: "$rootProject.projectDir/gradle/javaProject.gradle"
|
|||
apply from: "$rootProject.projectDir/gradle/jacocoProject.gradle"
|
||||
apply from: "$rootProject.projectDir/gradle/javaTestProject.gradle"
|
||||
apply from: "$rootProject.projectDir/gradle/javadoc.gradle"
|
||||
|
||||
apply plugin: 'eclipse'
|
||||
|
||||
eclipse.project.name = 'Framework Project'
|
||||
|
|
|
@ -9,6 +9,12 @@
|
|||
Module.manifest||GHIDRA||||END|
|
||||
data/ExtensionPoint.manifest||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/data/package.html||GHIDRA||reviewed||END|
|
||||
src/main/java/ghidra/framework/model/package.html||GHIDRA||||END|
|
||||
|
|
|
@ -23,6 +23,7 @@ import ghidra.framework.main.ApplicationLevelOnlyPlugin;
|
|||
import ghidra.framework.main.UtilityPluginPackage;
|
||||
import ghidra.framework.plugintool.*;
|
||||
import ghidra.framework.plugintool.util.PluginStatus;
|
||||
import ghidra.util.HelpLocation;
|
||||
|
||||
//@formatter:off
|
||||
@PluginInfo(
|
||||
|
@ -49,31 +50,35 @@ public class ThemeManagerPlugin extends Plugin implements ApplicationLevelOnlyPl
|
|||
new ActionBuilder("Edit Theme", owner)
|
||||
.menuPath("Edit", "Theme")
|
||||
.menuGroup(group, "1")
|
||||
.helpLocation(new HelpLocation("Theming", "Edit_Theme"))
|
||||
.onAction(e -> ThemeDialog.editTheme())
|
||||
.buildAndInstall(tool);
|
||||
|
||||
new ActionBuilder("Reset To Default", owner)
|
||||
.menuPath("Edit", themeSubMenu, "Reset To Default")
|
||||
new ActionBuilder("Reset", owner)
|
||||
.menuPath("Edit", themeSubMenu, "Reset Theme Values")
|
||||
.menuGroup(group, "2")
|
||||
.helpLocation(new HelpLocation("Theming", "Reset_Theme_Values"))
|
||||
.onAction(e -> ThemeUtils.resetThemeToDefault())
|
||||
.buildAndInstall(tool);
|
||||
|
||||
new ActionBuilder("Import Theme", owner)
|
||||
.menuPath("Edit", themeSubMenu, "Import...")
|
||||
.menuGroup(group, "3")
|
||||
.helpLocation(new HelpLocation("Theming", "Import_Theme"))
|
||||
.onAction(e -> ThemeUtils.importTheme())
|
||||
.buildAndInstall(tool);
|
||||
|
||||
new ActionBuilder("Export Theme", owner)
|
||||
.menuPath("Edit", themeSubMenu, "Export...")
|
||||
.menuGroup(group, "4")
|
||||
.helpLocation(new HelpLocation("Theming", "Export_Theme"))
|
||||
.onAction(e -> ThemeUtils.exportTheme())
|
||||
.buildAndInstall(tool);
|
||||
|
||||
new ActionBuilder("Delete Theme", owner)
|
||||
.menuPath("Edit", themeSubMenu, "Delete...")
|
||||
.menuGroup(group, "5")
|
||||
// .enabledWhen(e -> Gui.getActiveTheme() instanceof FileGTheme)
|
||||
.helpLocation(new HelpLocation("Theming", "Delete_Theme"))
|
||||
.onAction(e -> ThemeUtils.deleteTheme())
|
||||
.buildAndInstall(tool);
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@ import docking.tool.ToolConstants;
|
|||
import docking.widgets.checkbox.GCheckBox;
|
||||
import docking.widgets.list.ListPanel;
|
||||
import generic.theme.GThemeDefaults.Colors;
|
||||
import generic.theme.GThemeDefaults.Colors.Palette;
|
||||
import ghidra.framework.ToolUtils;
|
||||
import ghidra.framework.model.ToolTemplate;
|
||||
import ghidra.util.HelpLocation;
|
||||
|
@ -183,7 +184,7 @@ class ImportGhidraToolsDialog extends DialogComponentProvider {
|
|||
while (itr.hasNext()) {
|
||||
tools[count] = itr.next();
|
||||
checkboxes[count] = new GCheckBox(tools[count], false);
|
||||
checkboxes[count].setBackground(Color.LIGHT_GRAY);
|
||||
checkboxes[count].setBackground(Palette.LIGHT_GRAY);
|
||||
count++;
|
||||
}
|
||||
|
||||
|
|
|
@ -15,15 +15,16 @@
|
|||
*/
|
||||
package ghidra.framework.main;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Component;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import docking.DialogComponentProvider;
|
||||
import docking.tool.ToolConstants;
|
||||
import docking.widgets.table.*;
|
||||
import generic.theme.GThemeDefaults.Colors.Palette;
|
||||
import ghidra.framework.data.ContentHandler;
|
||||
import ghidra.framework.model.*;
|
||||
import ghidra.framework.project.tool.GhidraToolTemplate;
|
||||
|
@ -239,7 +240,9 @@ class SetToolAssociationsDialog extends DialogComponentProvider {
|
|||
private class ContentHandlerComparator implements Comparator<ToolAssociationInfo> {
|
||||
@Override
|
||||
public int compare(ToolAssociationInfo o1, ToolAssociationInfo o2) {
|
||||
return o1.getContentHandler().getContentType().compareTo(
|
||||
return o1.getContentHandler()
|
||||
.getContentType()
|
||||
.compareTo(
|
||||
o2.getContentHandler().getContentType());
|
||||
}
|
||||
}
|
||||
|
@ -298,7 +301,7 @@ class SetToolAssociationsDialog extends DialogComponentProvider {
|
|||
return;
|
||||
}
|
||||
|
||||
renderer.setForeground(Color.LIGHT_GRAY);
|
||||
renderer.setForeground(Palette.LIGHT_GRAY);
|
||||
|
||||
Icon icon = null;
|
||||
if (template.getName().equals(info.getAssociatedToolName())) {
|
||||
|
|
|
@ -19,12 +19,14 @@ import java.awt.Color;
|
|||
|
||||
import javax.swing.JFrame;
|
||||
|
||||
import generic.theme.GThemeDefaults.Colors.Palette;
|
||||
|
||||
/*package*/ abstract class AbstractSearchScreenShots extends GhidraScreenShotGenerator {
|
||||
|
||||
protected static final Color YELLOW_ORANGE = new Color(155, 150, 50);
|
||||
protected static final Color BLUE_GREEN = new Color(0, 128, 64);
|
||||
protected static final Color DARK_BLUE = new Color(0, 0, 128);
|
||||
protected static final Color DARK_GREEN = new Color(0, 128, 0);
|
||||
protected static final Color YELLOW_ORANGE = Palette.getColor("darkkhaki");
|
||||
protected static final Color BLUE_GREEN = Palette.GREEN;
|
||||
protected static final Color DARK_BLUE = Palette.getColor("navy");
|
||||
protected static final Color DARK_GREEN = Palette.getColor("darkgreen");
|
||||
|
||||
@Override
|
||||
protected String getHelpTopicName() {
|
||||
|
|
|
@ -22,6 +22,8 @@ import java.util.concurrent.CountDownLatch;
|
|||
import org.junit.Test;
|
||||
|
||||
import docking.DialogComponentProvider;
|
||||
import generic.theme.GThemeDefaults.Colors;
|
||||
import generic.theme.GThemeDefaults.Colors.Palette;
|
||||
import ghidra.framework.cmd.BackgroundCommand;
|
||||
import ghidra.framework.model.DomainObject;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
@ -37,29 +39,29 @@ public class AutoAnalysisPluginScreenShots extends GhidraScreenShotGenerator {
|
|||
// not tool for this test
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test
|
||||
public void testAutoAnalysis() {
|
||||
Color darkGreen = new Color(20, 154, 65);
|
||||
Color darkBlue = new Color(10, 62, 149);
|
||||
Color darkGreen = Palette.GREEN;
|
||||
Color darkBlue = Palette.getColor("darkblue");
|
||||
image = new BufferedImage(700, 400, BufferedImage.TYPE_INT_ARGB);
|
||||
Graphics g = image.getGraphics();
|
||||
g.setColor(Color.WHITE);
|
||||
g.setColor(Colors.BACKGROUND);
|
||||
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));
|
||||
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));
|
||||
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));
|
||||
|
||||
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));
|
||||
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));
|
||||
|
||||
Point p1 = new Point(447, 355);
|
||||
|
@ -71,13 +73,13 @@ public class AutoAnalysisPluginScreenShots extends GhidraScreenShotGenerator {
|
|||
drawArrow(darkBlue, p3, new Point(404, 88));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test
|
||||
public void testCaptureAutoAnalysisOptions() {
|
||||
showAnalysisOptions("Data Reference");
|
||||
captureDialog(800, 400);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test
|
||||
public void testCaptureBackgroundAnalysisTasks() throws InterruptedException {
|
||||
CountDownLatch start = new CountDownLatch(1);
|
||||
CountDownLatch end = new CountDownLatch(1);
|
||||
|
@ -92,7 +94,7 @@ public class AutoAnalysisPluginScreenShots extends GhidraScreenShotGenerator {
|
|||
crop(new Rectangle(width - 400, height - 120, 400, 120));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test
|
||||
public void testCaptureProgramOptions() {
|
||||
showProgramOptions("Analyzers");
|
||||
DialogComponentProvider dialog = getDialog();
|
||||
|
@ -122,12 +124,7 @@ public class AutoAnalysisPluginScreenShots extends GhidraScreenShotGenerator {
|
|||
monitor.initialize(100);
|
||||
monitor.setProgress(65);
|
||||
monitor.setMessage("Applying Function Signatures");
|
||||
runSwing(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
invokeInstanceMethod("update", monitor);
|
||||
}
|
||||
});
|
||||
runSwing(() -> invokeInstanceMethod("update", monitor));
|
||||
|
||||
start.countDown();
|
||||
try {
|
||||
|
|
|
@ -21,6 +21,7 @@ import java.util.List;
|
|||
|
||||
import org.junit.Test;
|
||||
|
||||
import generic.theme.TempColorUtils;
|
||||
import ghidra.GhidraOptions;
|
||||
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
||||
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.block.*;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitorAdapter;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public class BlockModelScreenShots extends GhidraScreenShotGenerator {
|
||||
|
||||
|
@ -95,16 +96,16 @@ public class BlockModelScreenShots extends GhidraScreenShotGenerator {
|
|||
|
||||
ColorizingService colorizer = tool.getService(ColorizingService.class);
|
||||
|
||||
Color c1 = new Color(0xE8F2FE);
|
||||
|
||||
Color c2 = new Color(170, 204, 245);
|
||||
// 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 = TempColorUtils.fromRgb(170, 204, 245); // light sky blue
|
||||
Color color = c1;
|
||||
|
||||
BasicBlockModel basicBlockModel = new BasicBlockModel(program);
|
||||
CodeBlockIterator iterator;
|
||||
try {
|
||||
iterator = basicBlockModel.getCodeBlocksContaining(addressSet,
|
||||
TaskMonitorAdapter.DUMMY_MONITOR);
|
||||
TaskMonitor.DUMMY);
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
CodeBlock block = iterator.next();
|
||||
|
@ -176,7 +177,8 @@ public class BlockModelScreenShots extends GhidraScreenShotGenerator {
|
|||
if (fieldFactory.getFieldName().indexOf("XRef") != -1) {
|
||||
formatModel.removeFactory(row, col);
|
||||
}
|
||||
else if (fieldFactory.getFieldName().equals(
|
||||
else if (fieldFactory.getFieldName()
|
||||
.equals(
|
||||
EolCommentFieldFactory.FIELD_NAME)) {
|
||||
formatModel.removeFactory(row, col);
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ import docking.DialogComponentProvider;
|
|||
import docking.action.DockingAction;
|
||||
import docking.action.DockingActionIf;
|
||||
import docking.widgets.fieldpanel.FieldPanel;
|
||||
import generic.theme.GThemeDefaults.Colors.Java;
|
||||
import ghidra.app.plugin.core.clipboard.*;
|
||||
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
||||
import ghidra.app.plugin.core.codebrowser.CodeViewerProvider;
|
||||
|
@ -74,7 +75,7 @@ public class ClipboardPluginScreenShots extends GhidraScreenShotGenerator {
|
|||
captureListingCallMnemonic(start, end);
|
||||
|
||||
placeImagesSideBySide(image, menuImage);
|
||||
drawBorder(Color.BLACK);
|
||||
drawBorder(Java.BORDER);
|
||||
}
|
||||
|
||||
private void cropCopyMenu() {
|
||||
|
@ -149,9 +150,7 @@ public class ClipboardPluginScreenShots extends GhidraScreenShotGenerator {
|
|||
|
||||
Object listPanel = getInstanceField("listPanel", copySpecialDialog);
|
||||
final JList<?> list = (JList<?>) getInstanceField("list", listPanel);
|
||||
runSwing(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
runSwing(() -> {
|
||||
ListModel<?> model = list.getModel();
|
||||
int size = model.getSize();
|
||||
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");
|
||||
}
|
||||
});
|
||||
waitForSwing();
|
||||
}
|
||||
|
|
|
@ -28,6 +28,9 @@ import org.junit.Test;
|
|||
|
||||
import docking.DockableComponent;
|
||||
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.app.cmd.comments.SetCommentCmd;
|
||||
import ghidra.app.cmd.data.CreateDataCmd;
|
||||
|
@ -71,7 +74,7 @@ public class CodeBrowserPluginScreenShots extends GhidraScreenShotGenerator {
|
|||
|
||||
Rectangle cursor = getCursorBounds();
|
||||
captureListingRange(0x0040be40, 0x0040be56, 600);
|
||||
drawBorder(Color.BLACK);
|
||||
drawBorder(Java.BORDER);
|
||||
|
||||
drawTextWithArrowNearOpenStructureIcon("Closed", cursor);
|
||||
|
||||
|
@ -92,7 +95,7 @@ public class CodeBrowserPluginScreenShots extends GhidraScreenShotGenerator {
|
|||
Rectangle cursor = getCursorBounds();
|
||||
captureListingRange(0x0040be40, 0x0040be56, 600);
|
||||
|
||||
drawBorder(Color.BLACK);
|
||||
drawBorder(Java.BORDER);
|
||||
|
||||
drawTextWithArrowNearOpenStructureIcon("Open", cursor);
|
||||
}
|
||||
|
@ -102,7 +105,7 @@ public class CodeBrowserPluginScreenShots extends GhidraScreenShotGenerator {
|
|||
// Make some room to draw our annotations (text and an arrow)
|
||||
//
|
||||
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
|
||||
|
@ -112,14 +115,14 @@ public class CodeBrowserPluginScreenShots extends GhidraScreenShotGenerator {
|
|||
int textStartY = arrowStartY - 4;// up just a bit
|
||||
Point textPoint = new Point(textStartX, textStartY);
|
||||
int size = 24;
|
||||
Color textColor = Color.MAGENTA.darker();
|
||||
Color textColor = Palette.PURPLE;
|
||||
drawText(text, textColor, textPoint, size);
|
||||
|
||||
//
|
||||
// Draw an arrow from the text above to the 'open structure' icon
|
||||
//
|
||||
int arrowStartX = 60;
|
||||
Color arrowColor = Color.GREEN.darker();
|
||||
Color arrowColor = Palette.GREEN;
|
||||
Point arrowStart = new Point(arrowStartX, arrowStartY);
|
||||
int addressFieldStartX = 40;
|
||||
int listingOffsetX = whitespace.width;
|
||||
|
@ -205,14 +208,14 @@ public class CodeBrowserPluginScreenShots extends GhidraScreenShotGenerator {
|
|||
captureListingRange(topAddr, bottomAddr, 600);
|
||||
|
||||
int padX = 100;
|
||||
padImage(Color.LIGHT_GRAY, 0, padX, 0, 0);
|
||||
padImage(Palette.LIGHT_GRAY, 0, padX, 0, 0);
|
||||
int y = conditional.y + 10;
|
||||
drawText("Conditional", Color.BLACK, new Point(10, y), 12);
|
||||
drawText(" Jump", Color.BLACK, new Point(10, y + 15), 12);
|
||||
drawText("Conditional", Colors.FOREGROUND, new Point(10, y), 12);
|
||||
drawText(" Jump", Colors.FOREGROUND, new Point(10, y + 15), 12);
|
||||
|
||||
y = unconditional.y + 10;
|
||||
drawText("Unconditional", Color.BLACK, new Point(10, y), 12);
|
||||
drawText(" Jump", Color.BLACK, new Point(10, y + 15), 12);
|
||||
drawText("Unconditional", Colors.FOREGROUND, new Point(10, y), 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);
|
||||
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);
|
||||
|
||||
}
|
||||
|
@ -318,7 +321,7 @@ public class CodeBrowserPluginScreenShots extends GhidraScreenShotGenerator {
|
|||
int y = p.y - 2;
|
||||
int height = bounds.height + 12;
|
||||
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);
|
||||
|
||||
int arrowHeadX = x + (width / 4);
|
||||
|
|