From 9a0d7892da92bff64deef973b5ddfd754a14f93b Mon Sep 17 00:00:00 2001 From: ghidragon <106987263+ghidragon@users.noreply.github.com> Date: Tue, 13 Sep 2022 16:36:26 -0400 Subject: [PATCH] GP-1981 Theme help and module conversions --- .../Base/src/main/help/help/TOC_Source.xml | 1 + .../editor/VarnodeLocationCellEditor.java | 32 +- .../editor/VarnodeSizeCellEditor.java | 11 +- .../core/register/RegisterValuesPanel.java | 3 +- .../field/AddressAnnotatedStringHandler.java | 5 +- .../app/util/viewer/field/IndentField.java | 3 +- .../app/util/viewer/field/OpenCloseField.java | 3 +- .../ListingCodeComparisonPanel.java | 9 +- .../ghidra/app/util/xml/FunctionsXmlMgr.java | 7 +- .../ghidra/program/util/FunctionUtility.java | 4 +- ...ryTypeProgramLocationBasedTableColumn.java | 3 +- .../screenshot/GhidraScreenShotGenerator.java | 8 + .../gui/ByteSequenceAnalyzerProvider.java | 16 +- .../data/byteviewer.theme.properties | 6 + .../core/byteviewer/ByteViewerComponent.java | 3 +- .../ByteViewerHighlightProvider.java | 10 +- .../byteviewer/ByteViewerOptionsDialog.java | 10 +- .../core/byteviewer/ByteViewerPanel.java | 2 +- .../byteviewer/ByteViewerPlugin2Test.java | 21 +- .../data/decompiler.theme.properties | 3 + .../app/decompiler/DecompileOptions.java | 2 +- .../FunctionGraphGroupVertices1Test.java | 6 +- .../java/functioncalls/graph/FcgVertex.java | 6 +- .../FunctionCallGraphPluginScreenShots.java | 2 +- .../java/ghidra/graph/AttributeFilters.java | 4 +- .../visualization/DefaultGraphDisplay.java | 5 +- .../visualization/DefaultGraphRenderer.java | 11 +- .../pdb/symbolserver/ui/LoadPdbDialog.java | 3 +- .../plugin/core/diff/ProgramDiffPlugin.java | 2 +- .../app/plugin/core/diff/DiffTestAdapter.java | 17 +- Ghidra/Features/Python/certification.manifest | 1 + .../Python/data/python.theme.properties | 19 + .../python/PythonCodeCompletionFactory.java | 25 +- .../Framework/Docking/certification.manifest | 19 +- .../data/docking.palette.theme.properties | 8 + .../Docking/data/docking.theme.properties | 2 +- .../Docking/src/main/help/help/TOC_Source.xml | 14 +- .../src/main/help/help/shared/arrow.gif | Bin 69 -> 0 bytes .../src/main/help/help/shared/close16.gif | Bin 859 -> 0 bytes .../src/main/help/help/shared/menu16.gif | Bin 62 -> 0 bytes .../src/main/help/help/shared/note-red.png | Bin 4182 -> 0 bytes .../src/main/help/help/shared/note.png | Bin 4193 -> 0 bytes .../src/main/help/help/shared/note.yellow.png | Bin 4170 -> 0 bytes .../src/main/help/help/shared/redo.png | Bin 187 -> 0 bytes .../Docking/src/main/help/help/shared/tip.png | Bin 1641 -> 0 bytes .../src/main/help/help/shared/undo.png | Bin 185 -> 0 bytes .../src/main/help/help/shared/warning.png | Bin 1354 -> 0 bytes .../topics/PlacheholderTopic/Placeholder.htm | 17 - .../main/help/help/topics/Theming/Theming.htm | 302 ++++++++ .../topics/Theming/images/ColorEditor.png | Bin 0 -> 25528 bytes .../help/topics/Theming/images/FontEditor.png | Bin 0 -> 10979 bytes .../help/topics/Theming/images/IconEditor.png | Bin 0 -> 6817 bytes .../topics/Theming/images/ThemeDialog.png | Bin 0 -> 47202 bytes .../main/java/docking/DefaultHelpService.java | 118 --- .../main/java/docking/help/CustomTOCView.java | 527 ------------- .../java/docking/help/DockingHelpBroker.java | 9 +- .../main/java/docking/help/GHelpBroker.java | 727 ------------------ .../java/docking/help/GHelpHTMLEditorKit.java | 588 -------------- .../main/java/docking/help/HelpService.java | 121 --- .../docking/theme/gui/ThemeColorTable.java | 121 +++ .../theme/gui/ThemeColorTableModel.java | 12 +- .../java/docking/theme/gui/ThemeDialog.java | 224 ++---- .../docking/theme/gui/ThemeFontTable.java | 120 +++ .../theme/gui/ThemeFontTableModel.java | 19 +- .../docking/theme/gui/ThemeIconTable.java | 116 +++ .../theme/gui/ThemeIconTableModel.java | 10 +- .../docking/theme/gui/ThemeTableContext.java | 61 ++ .../ConditionTestPanel.java | 2 +- .../widgets/dialogs/MultiLineInputDialog.java | 3 +- .../widgets/textfield/HexOrDecimalInput.java | 3 +- .../widgets/textfield/IntegerTextField.java | 30 +- .../widgets/textfield/LocalDateTextField.java | 3 +- .../docking/theme/gui/ThemeUtilsTest.java | 3 +- .../main/java/generic/theme/ColorValue.java | 5 + .../main/java/generic/theme/FontValue.java | 5 + .../main/java/generic/theme/IconValue.java | 5 + .../main/java/generic/theme/ThemeValue.java | 5 + .../theme/laf/ComponentFontRegistry.java | 6 +- .../generic/theme/laf/LookAndFeelManager.java | 13 +- .../theme/laf/NimbusLookAndFeelManager.java | 2 +- .../src/main/java/resources/IconProvider.java | 11 +- .../src/main/java/resources/Icons.java | 22 +- .../src/test/java/generic/theme/GuiTest.java | 12 + .../ghidra/graph/viewer/GraphComponent.java | 1 - .../viewer/AlgorithmTestSteppingVertex.java | 2 +- .../graph/GraphDisplayOptionsTest.java | 11 +- .../main/java/docking/DefaultHelpService.java | 5 + .../Help/src/main/java/help/GHelpBroker.java | 33 +- .../main/java/help/GHelpHTMLEditorKit.java | 4 +- .../Help/src/main/java/help/HelpService.java | 5 + Ghidra/Framework/Project/build.gradle | 1 + .../Framework/Project/certification.manifest | 6 + .../app/plugin/gui/ThemeManagerPlugin.java | 11 +- .../main/ImportGhidraToolsDialog.java | 3 +- .../main/SetToolAssociationsDialog.java | 13 +- .../screenshot/AbstractSearchScreenShots.java | 10 +- .../AutoAnalysisPluginScreenShots.java | 41 +- .../screenshot/BlockModelScreenShots.java | 16 +- .../ClipboardPluginScreenShots.java | 26 +- .../CodeBrowserPluginScreenShots.java | 27 +- .../DecompilePluginScreenShots.java | 65 +- .../java/help/screenshot/DiffScreenShots.java | 7 +- .../DisassembledViewPluginScreenShots.java | 3 +- .../screenshot/FrontEndPluginScreenShots.java | 3 +- .../FunctionGraphPluginScreenShots.java | 7 +- .../help/screenshot/GlossaryScreenShots.java | 62 +- .../MemoryMapPluginScreenShots.java | 182 +++-- .../screenshot/MemorySearchScreenShots.java | 9 +- .../screenshot/OverviewPluginScreenShots.java | 16 +- .../ProgramGraphPluginScreenShots.java | 14 +- .../ProgramManagerPluginScreenShots.java | 27 +- .../ReferencesPluginScreenShots.java | 21 +- .../RepositoryCustomScreenShots.java | 47 +- .../screenshot/RepositoryScreenShots.java | 70 +- .../ShowInstructionInfoPluginScreenShots.java | 5 +- .../java/help/screenshot/TextFormatter.java | 9 +- .../help/screenshot/ThemingScreenShots.java | 68 ++ .../java/help/screenshot/ToolScreenShots.java | 20 +- .../help/screenshot/TreesScreenShots.java | 6 +- gradle/helpProject.gradle | 2 + 120 files changed, 1540 insertions(+), 2846 deletions(-) create mode 100644 Ghidra/Features/Python/data/python.theme.properties delete mode 100644 Ghidra/Framework/Docking/src/main/help/help/shared/arrow.gif delete mode 100644 Ghidra/Framework/Docking/src/main/help/help/shared/close16.gif delete mode 100644 Ghidra/Framework/Docking/src/main/help/help/shared/menu16.gif delete mode 100644 Ghidra/Framework/Docking/src/main/help/help/shared/note-red.png delete mode 100644 Ghidra/Framework/Docking/src/main/help/help/shared/note.png delete mode 100644 Ghidra/Framework/Docking/src/main/help/help/shared/note.yellow.png delete mode 100644 Ghidra/Framework/Docking/src/main/help/help/shared/redo.png delete mode 100644 Ghidra/Framework/Docking/src/main/help/help/shared/tip.png delete mode 100644 Ghidra/Framework/Docking/src/main/help/help/shared/undo.png delete mode 100644 Ghidra/Framework/Docking/src/main/help/help/shared/warning.png delete mode 100644 Ghidra/Framework/Docking/src/main/help/help/topics/PlacheholderTopic/Placeholder.htm create mode 100644 Ghidra/Framework/Docking/src/main/help/help/topics/Theming/Theming.htm create mode 100644 Ghidra/Framework/Docking/src/main/help/help/topics/Theming/images/ColorEditor.png create mode 100644 Ghidra/Framework/Docking/src/main/help/help/topics/Theming/images/FontEditor.png create mode 100644 Ghidra/Framework/Docking/src/main/help/help/topics/Theming/images/IconEditor.png create mode 100644 Ghidra/Framework/Docking/src/main/help/help/topics/Theming/images/ThemeDialog.png delete mode 100644 Ghidra/Framework/Docking/src/main/java/docking/DefaultHelpService.java delete mode 100644 Ghidra/Framework/Docking/src/main/java/docking/help/CustomTOCView.java delete mode 100644 Ghidra/Framework/Docking/src/main/java/docking/help/GHelpBroker.java delete mode 100644 Ghidra/Framework/Docking/src/main/java/docking/help/GHelpHTMLEditorKit.java delete mode 100644 Ghidra/Framework/Docking/src/main/java/docking/help/HelpService.java create mode 100644 Ghidra/Framework/Docking/src/main/java/docking/theme/gui/ThemeColorTable.java create mode 100644 Ghidra/Framework/Docking/src/main/java/docking/theme/gui/ThemeFontTable.java create mode 100644 Ghidra/Framework/Docking/src/main/java/docking/theme/gui/ThemeIconTable.java create mode 100644 Ghidra/Framework/Docking/src/main/java/docking/theme/gui/ThemeTableContext.java rename Ghidra/Framework/{Docking => Generic}/src/main/java/resources/Icons.java (95%) create mode 100644 Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/ThemingScreenShots.java diff --git a/Ghidra/Features/Base/src/main/help/help/TOC_Source.xml b/Ghidra/Features/Base/src/main/help/help/TOC_Source.xml index 076b46b713..757aa6bf19 100644 --- a/Ghidra/Features/Base/src/main/help/help/TOC_Source.xml +++ b/Ghidra/Features/Base/src/main/help/help/TOC_Source.xml @@ -67,6 +67,7 @@ + diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/editor/VarnodeLocationCellEditor.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/editor/VarnodeLocationCellEditor.java index 293f5f88f4..a93be40e57 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/editor/VarnodeLocationCellEditor.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/editor/VarnodeLocationCellEditor.java @@ -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 registerWrapperComparator = new Comparator() { - @Override - public int compare(Register r1, Register r2) { - return r1.toString().compareToIgnoreCase(r2.toString()); - } - }; + private Comparator 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; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/editor/VarnodeSizeCellEditor.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/editor/VarnodeSizeCellEditor.java index 0d6dabeaba..cf52210291 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/editor/VarnodeSizeCellEditor.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/editor/VarnodeSizeCellEditor.java @@ -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; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/register/RegisterValuesPanel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/register/RegisterValuesPanel.java index bbdfc5b272..616d8f23ec 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/register/RegisterValuesPanel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/register/RegisterValuesPanel.java @@ -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)); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/AddressAnnotatedStringHandler.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/AddressAnnotatedStringHandler.java index 736b3195e4..38623b1fd7 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/AddressAnnotatedStringHandler.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/AddressAnnotatedStringHandler.java @@ -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)); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/IndentField.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/IndentField.java index 080c574dfa..6446826ed4 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/IndentField.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/IndentField.java @@ -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) diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/OpenCloseField.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/OpenCloseField.java index bdbd1069a4..8297462d28 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/OpenCloseField.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/OpenCloseField.java @@ -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) diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingCodeComparisonPanel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingCodeComparisonPanel.java index 43d49381e4..ddf2519d71 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingCodeComparisonPanel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingCodeComparisonPanel.java @@ -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 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()); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/xml/FunctionsXmlMgr.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/xml/FunctionsXmlMgr.java index d5887d3869..fca8dacd82 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/xml/FunctionsXmlMgr.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/xml/FunctionsXmlMgr.java @@ -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 list = new ArrayList(); + ArrayList list = new ArrayList<>(); Parameter[] params = function.getParameters(); for (Parameter param : params) { if (param.isRegisterVariable()) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/program/util/FunctionUtility.java b/Ghidra/Features/Base/src/main/java/ghidra/program/util/FunctionUtility.java index 0d100f86d9..cd86c27bff 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/program/util/FunctionUtility.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/program/util/FunctionUtility.java @@ -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); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/util/table/field/MemoryTypeProgramLocationBasedTableColumn.java b/Ghidra/Features/Base/src/main/java/ghidra/util/table/field/MemoryTypeProgramLocationBasedTableColumn.java index 996fae5b6e..48e46aed32 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/util/table/field/MemoryTypeProgramLocationBasedTableColumn.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/util/table/field/MemoryTypeProgramLocationBasedTableColumn.java @@ -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 { - 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"); diff --git a/Ghidra/Features/Base/src/main/java/help/screenshot/GhidraScreenShotGenerator.java b/Ghidra/Features/Base/src/main/java/help/screenshot/GhidraScreenShotGenerator.java index 21f04775de..8a298ec967 100644 --- a/Ghidra/Features/Base/src/main/java/help/screenshot/GhidraScreenShotGenerator.java +++ b/Ghidra/Features/Base/src/main/java/help/screenshot/GhidraScreenShotGenerator.java @@ -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"); diff --git a/Ghidra/Features/BytePatterns/src/main/java/ghidra/bitpatterns/gui/ByteSequenceAnalyzerProvider.java b/Ghidra/Features/BytePatterns/src/main/java/ghidra/bitpatterns/gui/ByteSequenceAnalyzerProvider.java index ce17de6f98..dee5126f7a 100644 --- a/Ghidra/Features/BytePatterns/src/main/java/ghidra/bitpatterns/gui/ByteSequenceAnalyzerProvider.java +++ b/Ghidra/Features/BytePatterns/src/main/java/ghidra/bitpatterns/gui/ByteSequenceAnalyzerProvider.java @@ -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; } } diff --git a/Ghidra/Features/ByteViewer/data/byteviewer.theme.properties b/Ghidra/Features/ByteViewer/data/byteviewer.theme.properties index 30ee3e4c8f..bdf0b37724 100644 --- a/Ghidra/Features/ByteViewer/data/byteviewer.theme.properties +++ b/Ghidra/Features/ByteViewer/data/byteviewer.theme.properties @@ -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 diff --git a/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/ByteViewerComponent.java b/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/ByteViewerComponent.java index f9eaf80da4..b798f81677 100644 --- a/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/ByteViewerComponent.java +++ b/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/ByteViewerComponent.java @@ -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) { diff --git a/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/ByteViewerHighlightProvider.java b/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/ByteViewerHighlightProvider.java index d26bf8f0b2..0b85bed4c5 100644 --- a/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/ByteViewerHighlightProvider.java +++ b/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/ByteViewerHighlightProvider.java @@ -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 fieldFactoryClass, int cursorTextOffset) { diff --git a/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/ByteViewerOptionsDialog.java b/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/ByteViewerOptionsDialog.java index f6c9f7e581..3a53335d29 100644 --- a/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/ByteViewerOptionsDialog.java +++ b/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/ByteViewerOptionsDialog.java @@ -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(); } } diff --git a/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/ByteViewerPanel.java b/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/ByteViewerPanel.java index a07e014a22..84bc547ca6 100644 --- a/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/ByteViewerPanel.java +++ b/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/ByteViewerPanel.java @@ -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 diff --git a/Ghidra/Features/ByteViewer/src/test.slow/java/ghidra/app/plugin/core/byteviewer/ByteViewerPlugin2Test.java b/Ghidra/Features/ByteViewer/src/test.slow/java/ghidra/app/plugin/core/byteviewer/ByteViewerPlugin2Test.java index a0962a6022..dc98e794e5 100644 --- a/Ghidra/Features/ByteViewer/src/test.slow/java/ghidra/app/plugin/core/byteviewer/ByteViewerPlugin2Test.java +++ b/Ghidra/Features/ByteViewer/src/test.slow/java/ghidra/app/plugin/core/byteviewer/ByteViewerPlugin2Test.java @@ -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 diff --git a/Ghidra/Features/Decompiler/data/decompiler.theme.properties b/Ghidra/Features/Decompiler/data/decompiler.theme.properties index d1cb359e89..2fe01e4b15 100644 --- a/Ghidra/Features/Decompiler/data/decompiler.theme.properties +++ b/Ghidra/Features/Decompiler/data/decompiler.theme.properties @@ -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 diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileOptions.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileOptions.java index 889b9348dc..b9f026dab5 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileOptions.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileOptions.java @@ -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); diff --git a/Ghidra/Features/FunctionGraph/src/test.slow/java/ghidra/app/plugin/core/functiongraph/FunctionGraphGroupVertices1Test.java b/Ghidra/Features/FunctionGraph/src/test.slow/java/ghidra/app/plugin/core/functiongraph/FunctionGraphGroupVertices1Test.java index d42fcd6a24..793f4d9906 100644 --- a/Ghidra/Features/FunctionGraph/src/test.slow/java/ghidra/app/plugin/core/functiongraph/FunctionGraphGroupVertices1Test.java +++ b/Ghidra/Features/FunctionGraph/src/test.slow/java/ghidra/app/plugin/core/functiongraph/FunctionGraphGroupVertices1Test.java @@ -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) diff --git a/Ghidra/Features/GraphFunctionCalls/src/main/java/functioncalls/graph/FcgVertex.java b/Ghidra/Features/GraphFunctionCalls/src/main/java/functioncalls/graph/FcgVertex.java index 76fe8770d0..e69ca9448c 100644 --- a/Ghidra/Features/GraphFunctionCalls/src/main/java/functioncalls/graph/FcgVertex.java +++ b/Ghidra/Features/GraphFunctionCalls/src/main/java/functioncalls/graph/FcgVertex.java @@ -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 diff --git a/Ghidra/Features/GraphFunctionCalls/src/screen/java/help/screenshot/FunctionCallGraphPluginScreenShots.java b/Ghidra/Features/GraphFunctionCalls/src/screen/java/help/screenshot/FunctionCallGraphPluginScreenShots.java index ea448159ba..3750614fd2 100644 --- a/Ghidra/Features/GraphFunctionCalls/src/screen/java/help/screenshot/FunctionCallGraphPluginScreenShots.java +++ b/Ghidra/Features/GraphFunctionCalls/src/screen/java/help/screenshot/FunctionCallGraphPluginScreenShots.java @@ -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); } diff --git a/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/AttributeFilters.java b/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/AttributeFilters.java index 8112de9a9c..5c41fb824f 100644 --- a/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/AttributeFilters.java +++ b/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/AttributeFilters.java @@ -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 paintFunction = v -> Color.black; - + private Function paintFunction = v -> Colors.FOREGROUND; /** * @param excluded ignored keys diff --git a/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/DefaultGraphDisplay.java b/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/DefaultGraphDisplay.java index 2af1108588..ad1dedf0e0 100644 --- a/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/DefaultGraphDisplay.java +++ b/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/DefaultGraphDisplay.java @@ -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 -> { diff --git a/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/DefaultGraphRenderer.java b/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/DefaultGraphRenderer.java index a4bf465f4b..5cee638e1d 100644 --- a/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/DefaultGraphRenderer.java +++ b/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/DefaultGraphRenderer.java @@ -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 diff --git a/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/ui/LoadPdbDialog.java b/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/ui/LoadPdbDialog.java index 4ce9f5d536..1255151e9c 100644 --- a/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/ui/LoadPdbDialog.java +++ b/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/ui/LoadPdbDialog.java @@ -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(); diff --git a/Ghidra/Features/ProgramDiff/src/main/java/ghidra/app/plugin/core/diff/ProgramDiffPlugin.java b/Ghidra/Features/ProgramDiff/src/main/java/ghidra/app/plugin/core/diff/ProgramDiffPlugin.java index 1ac4dac656..9f92c21017 100644 --- a/Ghidra/Features/ProgramDiff/src/main/java/ghidra/app/plugin/core/diff/ProgramDiffPlugin.java +++ b/Ghidra/Features/ProgramDiff/src/main/java/ghidra/app/plugin/core/diff/ProgramDiffPlugin.java @@ -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; /** diff --git a/Ghidra/Features/ProgramDiff/src/test.slow/java/ghidra/app/plugin/core/diff/DiffTestAdapter.java b/Ghidra/Features/ProgramDiff/src/test.slow/java/ghidra/app/plugin/core/diff/DiffTestAdapter.java index 95d9bbbf56..ac7f16fff2 100644 --- a/Ghidra/Features/ProgramDiff/src/test.slow/java/ghidra/app/plugin/core/diff/DiffTestAdapter.java +++ b/Ghidra/Features/ProgramDiff/src/test.slow/java/ghidra/app/plugin/core/diff/DiffTestAdapter.java @@ -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); diff --git a/Ghidra/Features/Python/certification.manifest b/Ghidra/Features/Python/certification.manifest index bbb9356524..412c40104c 100644 --- a/Ghidra/Features/Python/certification.manifest +++ b/Ghidra/Features/Python/certification.manifest @@ -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| diff --git a/Ghidra/Features/Python/data/python.theme.properties b/Ghidra/Features/Python/data/python.theme.properties new file mode 100644 index 0000000000..908b10a737 --- /dev/null +++ b/Ghidra/Features/Python/data/python.theme.properties @@ -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] + diff --git a/Ghidra/Features/Python/src/main/java/ghidra/python/PythonCodeCompletionFactory.java b/Ghidra/Features/Python/src/main/java/ghidra/python/PythonCodeCompletionFactory.java index 5e254f8efc..6943b2cbd2 100644 --- a/Ghidra/Features/Python/src/main/java/ghidra/python/PythonCodeCompletionFactory.java +++ b/Ghidra/Features/Python/src/main/java/ghidra/python/PythonCodeCompletionFactory.java @@ -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 diff --git a/Ghidra/Framework/Docking/certification.manifest b/Ghidra/Framework/Docking/certification.manifest index 5141d01af6..a36f1a8561 100644 --- a/Ghidra/Framework/Docking/certification.manifest +++ b/Ghidra/Framework/Docking/certification.manifest @@ -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| diff --git a/Ghidra/Framework/Docking/data/docking.palette.theme.properties b/Ghidra/Framework/Docking/data/docking.palette.theme.properties index ac6b3e892f..f645b4f5c7 100644 --- a/Ghidra/Framework/Docking/data/docking.palette.theme.properties +++ b/Ghidra/Framework/Docking/data/docking.palette.theme.properties @@ -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 diff --git a/Ghidra/Framework/Docking/data/docking.theme.properties b/Ghidra/Framework/Docking/data/docking.theme.properties index 6f786a52ba..eb75e8f09c 100644 --- a/Ghidra/Framework/Docking/data/docking.theme.properties +++ b/Ghidra/Framework/Docking/data/docking.theme.properties @@ -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 diff --git a/Ghidra/Framework/Docking/src/main/help/help/TOC_Source.xml b/Ghidra/Framework/Docking/src/main/help/help/TOC_Source.xml index 8a327c6107..9ec1fd5b9f 100644 --- a/Ghidra/Framework/Docking/src/main/help/help/TOC_Source.xml +++ b/Ghidra/Framework/Docking/src/main/help/help/TOC_Source.xml @@ -20,9 +20,9 @@ During the help build time, all TOC_Source.xml files will be parsed and validated to ensure that all tags point to valid tags. From these files will be generated _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. @@ -43,12 +43,14 @@ **The URL for the target is relative and should start with 'help/topics'. This text is used by the Ghidra help system to provide a universal starting point for all links so that they can be resolved at runtime, across modules. - + --> - - - + + + diff --git a/Ghidra/Framework/Docking/src/main/help/help/shared/arrow.gif b/Ghidra/Framework/Docking/src/main/help/help/shared/arrow.gif deleted file mode 100644 index bcb3db70578a43a6aef37fbb13aeb0b3b0ca81be..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 69 zcmZ?wbhEHb6k_0GXkY+=|Ns9h{$ycfU|?j>0r5dH3`{aT{VPwu<>#Lfcu7j_z|Sb& THV==isgesP{Mz8g$Y2csRcRCu diff --git a/Ghidra/Framework/Docking/src/main/help/help/shared/close16.gif b/Ghidra/Framework/Docking/src/main/help/help/shared/close16.gif deleted file mode 100644 index f5238646392f8e1bc4b29109b96a7244fbf17182..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 859 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 diff --git a/Ghidra/Framework/Docking/src/main/help/help/shared/note.png b/Ghidra/Framework/Docking/src/main/help/help/shared/note.png deleted file mode 100644 index 51e1c8f8c1fa1688a64436ed366e3dba15a6b750..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4193 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 diff --git a/Ghidra/Framework/Docking/src/main/help/help/shared/note.yellow.png b/Ghidra/Framework/Docking/src/main/help/help/shared/note.yellow.png deleted file mode 100644 index f8ab6489f2afca01576947572918d51cf3fa7978..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4170 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 P&=3YsS3j3^P6Eyh)6Gs@ zkuH#=l-3Vj=tBr?aOgrIebHNKUeXuSHqbT@2!=vwFqBgK(n6XND2+?n*xB65R%I!c zEJasWSJK>V*7JR3lvk7V-devo-{C9+^W$SU3yBE&Vs>1~WBzdl`_>`vO$=Yn(FeT= zTsd0J!|*geF-0XPEei}QHlu!5D9h>f=D5v z(44pM#t%qrb6Gzad>#WUj1NHJL*Tz1JS*Mkk!$0hJIInfzkBttS zdJ%2*E3JI;%NrEMC-N)#Pw*jVd;s$1kuli`pUJAjYvNilnRO;600g4jyf3>o*mFAh zz_8I_bL6Si&y&+;EkR0#wn#V@pUoT^|qBR5R|W=2m+GZC_qdsEK8x9cvQ*C ztAnVo{RY4TeF-20K!n6}44Xzq#cc3^ zi>Z53di$9p`1hobkG>oA^bg1zN5{x;nFc>)LpHmFj4ki5Me-G>qO_+PjU5^us5ONg6~|;bbhArT)*wwNO)$d0-raKsNLlkBkZHZr zAUp1Y1X|{sjmA)ExY~(a2n?$yCv-fqN z;#{FDbC4{gVzzT42q}PIgpe~4bESfGtHXLAh>~Ki-PN|ETPX;DbaRR4nrK}ISv1$P z0NuN|NLtmcxa$)$2m%6Ru+oZ_*Tg3haXsR4+0Bb~HxlL8#sGq%k8C63)t{|X_^*sC z{GqRvkV^iq)z;RS%2OMF6h#pWGj_XFJZg7b-rLCCeU&5uiJ-uwO6}B!;M;%3$(6J} zfvofk%9EU0VHKP!K%|HRqa4pKMqbe-Frt)|w!j1yD#XNqI;}vp!U_fcZ2x<~=Wb+F z9hg;H^@EheE`)a-JXd(Vx!=FB>X)vrx}|IHmdcZdR3su0VMr1?s$M$?-+uuoS8D(F zZYVDgy~IIqfIo8As&bqhMAGVNT`6-;AT&a44^^`ag+OK$)oY_v-zd>cc8|5G|He%AQutjm?QM0*^v^9rME;sf>;PvH^i{onJTUg$_*OL|*TO%Ms znSsunfh65L_9ven1+XUqKYnqdwswo1r=I*OMu$AiUw#$L2FFj#;ERtwj>pdaP=Dd! zGnb#(D*-lUrPYb~?S&mRIdvHM{)a#yln$N5%9g@*GeHzZx6lI$y+Fw0Bte>{@IAjU zf~7U?zyAbU%_i#gI@;~FgT2ZCfl`wjilP`vnqYZ(8QS#$0!l#uOQ|9Q_MT;+I0AW| zmz9DN3LgLV6R7SR0yAT2X$g(3h7eJ6&+di=r$2q_*iP7d>DaMj<3TBa6k=KdfiySY ns8$E)H%9 diff --git a/Ghidra/Framework/Docking/src/main/help/help/shared/undo.png b/Ghidra/Framework/Docking/src/main/help/help/shared/undo.png deleted file mode 100644 index edd57f5c15f02bf0fd9b572f42bcd1c67ed088ef..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 185 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!73?$#)eFPFP2=EDU{r~?zkjc=%(9i&)fHaU; zv}jR7Ljy?SKLc1ENPt*CpgCP>15li?B*-tA!Qt7BG$6;^)5S5Q;#P8k0^bJiY0RE( z3b71HhFly>I~~qwL@)^HGZ?WrykcqKnIK|4gHc4QMe2Zt!xUczBN65o6N)Xj0gYhr MboFyt=akR{0Pznvr~m)} diff --git a/Ghidra/Framework/Docking/src/main/help/help/shared/warning.png b/Ghidra/Framework/Docking/src/main/help/help/shared/warning.png deleted file mode 100644 index 8e9133789b0c5f9a2fd7cfd29fce85a35e8fa120..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1354 zcmV-Q1-1H#P)E2L1Q&_V>FBm^-K zqXA4L(MT}7Nc71>ebr!yF~$TFjp&0f`pyGBX@Z#WqA!LtVgSFQAzIM3(C+TcZ0GZO zr#p3;-Bd|wC7$G$b29gyIsbEJ=VM7qiF;{G@jn)DmiRn(VavY+pHdOM#pCd2YY1=- z`{e9@C*VBgBAtI7wVxORB-rf7msy*?8#|=4`j_A$iAZBF26pGD=y)2U?J$BFeKfMf zO91iJ7Z4yPUG}nQe~Msq0GS?$j$;(rcm)@bUo`<~_u|#i>*`{?2LZ1-59@mjp;PFD z5GUh3t0$m_+Wvzj{;E*fm=gVS2z~@xf&2v!E);%(Utdo_FkN`asqs;^&NVGm-YjZ zHD8RgsuEx!bZX$d3dooWc>69TiVnd=-^vI$OL}0j_emel@=PBPHHvYe1gPRtO+n}t z05yd=Zsq|okO^$|VVrw+0*DA5*bvs~QQo;5AWM-HQblFmR#-|v6`%ynK$M0M*mNMy z1raS@z-Q3?fSo~htIo|#Tv$q23IQIiwfwoKmL}=5t zdlug#%3A=cGz72)+^B*)3*=j>V5M=KC+jo|t=tR0hCR5YZ3C8`g1}Pt2@J0syvy^$ z8W&RlbU-rz5{e8U0+{6-h--kZ1Gxx*Y#;$n1e%%<-t5E~{d4Ta<8bd>z$s$bCm!~0 zuwCYT)4<#h044LHm^jG8`z;UY+JI95++-~_*>GBSH|kg9PS(Tsic|C@UYF6G2^fJ< zzZJgC+IqMs{Qh_0)JMu!k?Cr53$8GcSSmmm(_p1;=FLu?79)5FK>ZGW1H{5ke%QO6 z9n*VVKt}^3!A!gc^gmle|M6O~-h{5uLYPAuyh$1j+xVe)vj6@;a+9*w7Z|5C8-{b6 zeg;_5Axoe%c7D{sG7{!Gf1&Hxr$n)5u*U}=r>_5$--_+cA1$o;8_yqS$NWmiMsr;i z&=xvLSk&pvg&c04E@I-160C7^q3d+NNZ=7N_Y>SnZKHpK9R-LjEnpBi=em4N-`GdI zFbN2xXsM5j`M(Nq!aUwDT3VPdniw@;+3PU3u7^7JNnAy?NkH1YJHcKEoSs2*Xfj@D zVeyIE57uFHsqO|s6b{sZ2(8G4SR%72fIDpt)~~VW<65o1fs0g}`G&3;+aBo2Zoag* zUfFaVm+a#!;ASmrLHfnyzq&|rg(*3RZ1bA7P8?peaN3ECO zD&^Ek#qSEtfcW`0mcBAnf{sc=AeHhz8zbi17A@AzSsdxu0^wj3XXKM zTq0&F4i~B>F$-lpVE{Lf$7PI3yJ-SV?|~5zR>Q7BHop(<`QP)u0Nfb>!wJQD)Bpeg M07*qoM6N<$g1EwNvj6}9 diff --git a/Ghidra/Framework/Docking/src/main/help/help/topics/PlacheholderTopic/Placeholder.htm b/Ghidra/Framework/Docking/src/main/help/help/topics/PlacheholderTopic/Placeholder.htm deleted file mode 100644 index 13961916f5..0000000000 --- a/Ghidra/Framework/Docking/src/main/help/help/topics/PlacheholderTopic/Placeholder.htm +++ /dev/null @@ -1,17 +0,0 @@ - - - - - Placholder Title - - - - -

Stub

- -

Blah blah

- -

Related Topics:

- - - diff --git a/Ghidra/Framework/Docking/src/main/help/help/topics/Theming/Theming.htm b/Ghidra/Framework/Docking/src/main/help/help/topics/Theming/Theming.htm new file mode 100644 index 0000000000..7046251bc1 --- /dev/null +++ b/Ghidra/Framework/Docking/src/main/help/help/topics/Theming/Theming.htm @@ -0,0 +1,302 @@ + + + + + + + Theming + + + + +

Theming

+ +

Overview

+ +

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

+ +

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.

+ +

Theme Dialog

+ +

The Theme Dialog is the primary means for creating, editing, and saving themes.

+ +


+  

+ +

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.

+ +

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.

+ +

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.

+ +

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.

+ +
+

Color Editor

+ +

When you double-click on a color value, the Edit Color dialog appears.

+ +


+  

+ +

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.

+ +

Font Editor

+ +

When you double-click on a font value, the Edit Font dialog appears.

+ +


+  

+ +

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.

+ +

Icon Editor

+ +

When you double-click on an Icon value, the Edit Icon dialog appears.

+ +


+  

+ +

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.

+
+ +

Theme Actions

+ +
+

Switching Themes

+ +

To change the current theme, first bring up the Theme Dialog. + The Theme Dialog can be invoked from the Ghidra Project Window menu using the + EditTheme menu. Then select + a theme from the combo box at the top of the Theme Editor dialog.

+ +

Modifying Theme Values

+ +

All the colors, fonts, and icons that have been externalized can be modified using the Theme Dialog. The Theme Dialog can be invoked from the + Ghidra Project Window using the + EditTheme 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.

+ +

Reseting Theme Values

+ +

To reset an individual value back to its original theme value, invoke the Theme Dialog using the Edit Theme 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 Restore + Value menu item.

+ +

Reseting All Theme Values

+ +

To reset all values back to the original values established by the current theme, invoked + the EditTheme Actions Reset Theme Values menu.

+ +

Saving Themes

+ +

After making changes to one or more theme values, the Theme + Dialog's Save button will be enabled. Pressing the Save 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.

+ +

Deleting Themes

+ +

To delete a custom saved theme, invoked the EditTheme Actions Delete Theme... menu. This will bring up a dialog + with a list of themes that can be deleted. Select the theme to delete and press the Ok + button.

+ +

Exporting Themes>

+ +

To export a theme so that it can be shared with others, invoke the Edit Theme Actions Export Theme... 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.

+ +

Importing Themes

+ +

To import a theme, invoke the Edit Theme Actions Import + Theme... 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.

+ +

Reloading Default Values

+ +

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 in the top + right corner of the Theme Dialog.

+
+ +

Theme Property Names

+ +

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.

+ +

The general format is:

+ +
+
+ [type].[category[...]].[client].[specialized uses] + +
    +
  • type: color, font, or icon
  • + +
  • category: any value, examples include "bg" (background), "fg" (foreground); + this may be multiple separated values
  • + +
  • client: the plugin name or feature name; any value used to represent a more + specialized use
  • + +
  • specialized uses: any key value here that applies to the client, such as + "vertex" for a vertex background
  • +
Examples: + +
    +
  • color.bg
  • + +
  • color.bg.listing
  • + +
  • font.button
  • + +
  • icon.refresh
  • + +
  • icon.refresh.disabled
  • +
+
+
+ +

Theme File Format

+ +
+

Saved theme files have the following format:

+
+     
+   
+        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
+    
+   
+
+ +

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.

+ +

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.

+ +

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."

+ +

Specifying Color Values

+ +

To specify the value for a color, there are 3 acceptable formats:

+ +
    +
  • Web Color Name - Examples: Blue, Red, Black, etc.
  • + +
  • Hex value - #rrggbb, Example: #ff01a4, where ff is the red value, 01 is the + green value, and a4 is the blue value
  • + +
  • Hex with alpha value -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
  • + +
  • RGB values - Example: rgb(12, 34, 56) where red is 12(decimal), green is 34, and + blue is 56
  • + +
  • RGBA values - Eample: rgba(12, 34, 56, 127) where red is 12(decimal), green is + 34, blue is 56, and alpha is 127
  • +
+ +

Specifying Font Values

+ +

Font values are specified as follows:

+
+        familyName-style-size
+   
+
+ +
    +
  • familyName: the font family name such as "monospace", "dialog", "courier"
  • + +
  • style:either PLAIN, BOLD, ITALIC, or BOLDITALIC
  • + +
  • size: the font size such as 12, 14, 22
  • +
+ +

Examples: monospace-PLAIN-12 or courier-BOLD-15

+ +

Specifying Icon Values

+ +

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,

+
+        icon.refresh = images/view-refresh.png
+        icon.push = [EXTERNAL]images/energy.png
+   
+
+ +

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.

+
+ + diff --git a/Ghidra/Framework/Docking/src/main/help/help/topics/Theming/images/ColorEditor.png b/Ghidra/Framework/Docking/src/main/help/help/topics/Theming/images/ColorEditor.png new file mode 100644 index 0000000000000000000000000000000000000000..937e5a7b70e65771530fab62797173f26b41711e GIT binary patch literal 25528 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 RfMyFBJYD@<);T3K0RW;r$~*u7 literal 0 HcmV?d00001 diff --git a/Ghidra/Framework/Docking/src/main/help/help/topics/Theming/images/FontEditor.png b/Ghidra/Framework/Docking/src/main/help/help/topics/Theming/images/FontEditor.png new file mode 100644 index 0000000000000000000000000000000000000000..10e4350d97272828388c39b4c5a993b63e5cf6af GIT binary patch literal 10979 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 literal 0 HcmV?d00001 diff --git a/Ghidra/Framework/Docking/src/main/help/help/topics/Theming/images/IconEditor.png b/Ghidra/Framework/Docking/src/main/help/help/topics/Theming/images/IconEditor.png new file mode 100644 index 0000000000000000000000000000000000000000..b8065900331e9be5fde8d2e30117002b6eb3bca6 GIT binary patch literal 6817 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 BOaTA@ literal 0 HcmV?d00001 diff --git a/Ghidra/Framework/Docking/src/main/help/help/topics/Theming/images/ThemeDialog.png b/Ghidra/Framework/Docking/src/main/help/help/topics/Theming/images/ThemeDialog.png new file mode 100644 index 0000000000000000000000000000000000000000..5254f290aa44d150d8fafa1c4ab2e49709b48062 GIT binary patch literal 47202 zcmd42byQr-_BBdE0!gqW1h)_p2p-%m1b24{?hcI;LI@BnxJz&vcYy{l@~T64{ss|b>p6~lN!_yh?F2}45sgCY_V@+K0} zz2issfh**a-`$arJ|Rhb5K?y4-AzIDd@+97#qpUHao_j#vHbnd>ZvFBFScIN+&|31 zi^w>hfBg8%yobtn%9N+>XkRezDT-6YL=FjMWws%{(@u6nd7}DbjX+0NUQKBY9)rn_ z>jNoKb1XZNrkqa4W39qe*I1;pJ3g+1O`qKNMDGRw{FEolG|hV5xLjHTGK?Kzet-jSuhMpBA`A*I(nA zEHgvCBx{W2L~%T_7~`;-uXF3gM2637!0Z0BLMo**oD@0JbBNWnp&&PXva~eTQ!zjH z&Sh8q6eGcnCu*c~(fZ{v{a>w7susQQQhp40e6vkHH^zVFKw6<-z{lQoAWsT5_>i~ zU+Q9b4JiqJiE1oXT%k-c2h8*>!Hw1fOXvo?Nl<>$n{!uF9$Lgc-;^1f_Qp(bw3hZP zA7sj3XrC*X`dVg%e~wWXb)uXl&(g={-4GfFhvEvuS0p4smrw4XQwf(o|D@wK*Vn6^ zw9G<{<>md>d^7ObC8DMyD6iRp1TLqWlJr>us#eIMHMf(O6l3!(TA0l2^Day_shbzg z8@ug$U9j}bqJ|q$2CQxNm6yinF6R)k+zUs!l-H83E2K}O$DF1vbw@#IA+^$@v^6m2 z#8T_!uWt{{d0k0Tj&li6;}N~#82HOIPcs> z-PXBmbc_NHc($}F&+nmyS6Iy#yzi{KhxDnP#YE;cU<#P@t0tndh?!%wNwTKRxkmPy zgZaK(r@)I+NXmV;NY>D+F?xH=ygHZc_=CnMuFkheNWb_i52hhRXR=Xe%oMhc9Hb%W z{5~hGE4dsn-uQZ^@0W{@i85xctWJ2IM>!nLujKmZ)VZ9+S+@dHPF^OGn!6ywN|hgT zM+EMF*>O5<8((Pj#+-6FzP=HYgY$WobG*B~vS;LVj?9aloVi+nG#;%s&KI=bau^sM zh6?<0U_Ca-m8g#)adRB(M|krhCKpz#ojmN~@~>jlKG@vgrEV@(IL{8_nfe?am#h+< zv7>&dI-2ITH$0B7S`v_*f97zz-QMLxsdM2~ruVd$-3->GKr7ri2ZUCa$!;BKzQU9XDU7Omy|k&y(e-%r;f z-nlWd9I~Gso(OQuO|hTUV6)y{!AZIlY^DU--<| zc2O9>O~-tW0EaCST9BLKGn|tk6!17r zp4m~&tP!Q0Pv4sHT5`fodChN05#4Km>On!oY$#@yww z#7a_J;rG<|Uh9E`w73uWxg07Tw@?cARbh%3OxBB+0Ee8B5M=xn@cpS11xfULpX-R4 z^<5(79upldHdi9$B_yQpMP_V(J)zBMwMhgq);4JmyAs2pzqDrkn@p$Gvw^r}BdJaW zyk*>N=9ao3eDu6EGgl_S+Kk17$PzkvC00pPBTUu3zt-BbR&{)NXFhW*jXGI@^z#lc zKZks4uWI@Q0{4UMIWf80RW9L`;_8#>A5Mm*rf5eRco%!6Cw-3Fwp1pzoFaZ*atI4% zrE-;5bEi#*4~}&9#CiVd_j;UKaZxzfB`27(UiHH^1eN*%>5*dwfM z)h*%uJ@8tr+qICt7U3Y&IJACp+in@iy~yuNN{br@8w?*@wKMMy3Ne#S)GGZ>dS|-W zKRzcOK4h$S_iTG|Yr_$a7s`VuOsR7j#s)|R z7rheV)a~kVeKf>2!1lFaiCdSMGzt9`P4A~SzO32R^}S;^3vQR}q;)VK3Jxy;`@!$o zrs^-QX7}m9XV-?THq2IhJYzH`bUHZ~75TEyap)VW8?G7p>vGyBMx!--24gmA{S5Ly zrsVEga(g1vXWrDN9238HaW{DiZ4)bew8iSS0HWaFASl1}NloIBn-e%RZYv(W)^cq; zzk>CPS`pW^r18D0JUGpM7;O1FZ`A4WT0#RDvDlvfYdy)TxV2v!w*-5qc$defIPYZ; z0Su&Ni=9P(G~%3#Xt0{7B70|b?^!+~Mt?B>98R^1n{p4SWhtSIjr>HeqXS(B*WmRX zMSS`PcvB17qU9?F(jh4t=<_J;y-dLC(mPA_{CtwjO&7Ml%=+-U?{Atz*Jh76(rk7r zyjCTvr~nT_rL_5$5_|c>V`EDU<@R|p!>Z`Oi}%0n@72-c2)yN!2!1);=G9}5M2v;0 z*C&=-b0#E&P4D-BiA@I_5E6d6iE118(ej@nNO51g6Fw#c{b@(ncblGx(72t)Qb@U#m$W;m0tTLH}$QN?&`(lJqpvfFevqv`K3@5xcx5M;(UbLZ+`?h54ft zaX0)}u9YN{yM0YZBa1$U`tM16?1|Ng@-+2q&Z}r$&_AEmpEAr_KjI>&A)f2^8u%hs z89!;#9#Lda%m44L&~352C*KY0PwBr)X2EA5LKj2-`UXW9uf3KnN@OGC^S?~hYjIkNVqCEs<8x`XYIF+gI|hCYhS=&`w|igTD7&T7~{3*l5&y2X$j6wqUv9TTH6TD@ne$ zC#NNFVob~z$j*ZQT6P?Q8uRjx>8;(9Tb*k22zLBp!emTN0$hT5(wpMVLgarhG32YT zmR6sIBPHEut!m;qzD|%TTgf0s6G}U>U+duh&GkSO`(S*zaV&oEe;yK5SB7>8^XE?$ zjDY8e!X7?M;ldLV=qiklw>9DyE^W1I{cb27#z)0`{qRiu-|i7R*L7c8vyPCPaGv;N zVq?Cl2g|K}(a`n4r~8G)yK5`6-J`vqdFo6wIzzR8oL&DTJ8a=pwHbs?q35*y)f%P! zqu-6}&}DuHL^H>0>kDHV2G z(1*>R3JtC+ACjQTNyd?VmEBryEZr#Fb6VLv4LbXcMJ?V!D-5Q#`nxUT~h+tx;GsttwU?w zHcnFn1Y@y%mU<@qa)nnSLsyN|uR(kbTa+V3FW`)KJ$>Rcpbf2W)qfhQ;SV3i1Gwtr z(LDPaPN(dQ;bo%%4d&3zb16#@86Rn^{%bMel|&a3QH}_kY*(K@294;mfnqQ-)o&)N zlmaYks#~piUip3S^`ak^3hwwzoscuuudG++ihT^+vy2~^B$_WD?(&dW^Da0MhWv@* zun#62Fk;~uSsk{1P4yN29*^tnRZB^;2sVp}#Af}nk&HtLJnZ)&LNODPrrk83DVFi9 zq0M{hwFjOjH-CAX-D|5n`!r6sIxK5+C53~=EA+Pt8Ai9)4-?t1 zU{>M}&Zz2>w4UMNO}e4Q9`6a{3Hp8(3C7VXvqRQC+87eXgCbeQ#UO~ZZq@FhkuaSD|=ZC>Kj7{EtjG%o2p4Pd>%8CldD@>A3W$@NE z+s=af&K|GZVKk%O$BA7xNwR!_G}$z+-1q(*hwJ@Ux2R+zJKH@+-QzHaA|V zVe$cU6`*D;{`0_TR4kH%EmM`VOEP{p?yO-BnI`yD!c~pM9K*g<{9tX6+tIq^Xf+>E z^T`Cy77EQEtv?$~SS%#RM&)tm$Y5;IOvv%Es;07E^&WRJT6ij_xkZ!bMIM{!O3i{> z9FKC5>dkp~#RH7XW0Lfq^u)TKKl}AcIcX?ArXk4do@R%SrOE9~B9`}uqn0Adysj{= z_cOy2ygI~7XjBSFxE)?`vX)DXnxt@p)^0C*C!X{X%ldcpU06@%e0n;ttR=e9FYdvneQ+tCDv9J^d-G&Ee0?+WJ z{l1stuW_msYj2^H`(hT_ld(D}YcCtWc_Aermx{>%_<+3G>zX_i)6ebZPYI&ZiAURN z+hL8UQ?8S#$gpgw0_MD&&TlwmYkQ8XuIYz)f%vi>ZIF)9~QL z5~H+G#IrMDDPNlIn80l8qg$uFMR78j_HFjnrBs)uZ$f)nMZ~D8m0ks-xJUt+`wIr` z^I7(k4q>+2Gm3u=p?bbHj$X@V!I$E4X{1c`&dD3$pFR$k;Y9;oKYAQYt&_9s`)LLG z3bPWzI#o8C7Fq+Jfx~nYL-YNxxbXTRV&Q2Dkmm{;QMe2m>&x3sJ4*<*`YwZ*Aopz>~QmdD$B)NQyuWWzXzj7e+rey>Y)aSFyx}!o-pvoT%}21$ zBF%xf!14gWe=OZmxyCa=@`x=}uE`?&UoaWV2CGK5g!=5xbFeQddMZBo z<>=BQifOxdyP03DhksLq-}&ZaR}fyPM;z=X!;n!|y6Ikd^61Bd3h&t!5HCc0Vi)ap zvllO|cJx1);4?(0t?c_b@Q^@BCe=WWc0F==@?svE<0Ne18NTgn#|c=$LM zn}>JVqf~6DwwIuuy6`G%A*VmtXXVuE``VXI%0WFJeYv5B6y|eXxfg-iPS~A1zh!d$ z+;sc63LrTzZht333CW$mIiB8MwXE@JK6_w_;_hPZeOqIRbJ*Rj*A)#+_vGS#|*if zsZ_FSe*ECnHgn>gn%M=~nQrctYpi&xiA3**D=bwW;h!J-n@J&AJw;=MjSYOhv(g+h09Bs zU5|tCuB-QU=Pi-wsd;I3@NzVtyZ_ll&34=my^8+Xd#lyFc9FHZ1Ad->JTN-EA zwzHHgCb!qJRV6i6bIv=bI!Oj6i89tRpt5}b;mAu&e1>_0-;aG?Q%|=)J5SdY)K~B8 z+^O!KHxz=w#~y_T{&e4qm3j7@@WQ%+oxSRvrebg`N$1}?>!eZPBT0Bc7-yA~9Ohcq zhK5&?rTz1xl6GzU;Ry%5mBmLZiDi0}-`!}0JtREKz~Sd3aUOn!kB%~I#$VaC8ok3V z?#I}K6RLqmFVWEXyXDK?GQkzt`oV(X$H$t|Db1Chog9v913JamMEpt{ftZqc_IRP| z!n6&^o;qm4nn9QleNFu$?$8euU6siu@1>sG-fiLm)yjcxsJ1@(Vqe~_Fj{ZI+L1xo zb@%v!CmaP)_ff3qQtunTWS$Dn?^iipB;q60z8sKdMw3vqU)-3C(IDD{yKy90^>$2u|hiau74=usi|?`;i(Z0Me__M|CgvDF>D)eWK((Fdg4mnc?@_m39O9LoP3}NFqIN-fab~}p1+YCa)*Q^{R$z_Tv>s|gkFzbcqL~dUxrfivRPtwXZGB+S^j5sC zFjP^It~0p6W@b2lETz38ZMogj;D%uoCNb|wt-TU3;>>W%AW_UgF1w*2Ie`L)^It(M zroyvTJ1rpuIzJBnxng*9r>7H{9~E`I*d}xQOhr6~m)~p@&9p%rRnKwh6~2Y$Z%I!s zZG?KblH%25=v-lH)wz+?i2uN$)Da(EP7CkR`$jM~Cj|!#2=O>2eXCQ44&wKs{1Tea zG7v?DRYU8DOREd@IG)0$8h9J}3f`!v!|LEVz--(zM5rvrQulr(?)Be8V^A;a5TIn# zHX^}{^QB^y&55*UGmW`Z@hGguhIhuBa(x%Skx=g1?^7`D&gbK6@Mca-I~K5}2$mQBfIa7)O!j~$xZHNGsowjBF*zlrH=4fwcrvC(SaRO|ie>oM5?4&{ zl97`?gz+ECcv~mcS#{-?<*Ave8|O_SQm<9^T*+tp7evWhS1-#|f<1>}R2=n<R%f|F?bIs`1U`?j*d+hU7T?>s)=J_mX++18i63fpSZmxSB)YQ}t zo@N?khl@}IP%$dy&=mRUy#%IAT2}9W;*96R-lBOA)nH%#3TSWR zfkm-)c26vv&*3@CZQacWKT3KuPP|uUf=VX2cXksMD9MlJRf9o|d(?=Tj3W-eKiB-I z-S+0rcp-t`EEj%cN`ZmafaNgBm}mg4&6C@wHIieYFquj&wa2!x(c*-gNua&)m&ccX8yRx zz!cEv$R5x7sanmG3?|DTZ-Q#@f8(1G7p@u9agDyrMDIJjFl|U!qrM*}Pu%~GrKv2S zGUr?JYV@La+n$jahdu~3Nmdj3%6}}dM_95A(fu|n07SZ5K? z_IqdYhLy~v;~Nq9@8JLqJU%o09vhI2o@NrB-Ta9ABi|uu1S*7HweHjYx%-K( zBM&myo&WC7pD)pv!1j0unZNG7WciOS`P~<&ynkN%>oI9aG~@mM@1NRU{}FRfX?61KMR?~W16!y)DSrV=XH z%?x@cCTleg8v{CJ2H9>$ctMyG!UzzRFxsfxLt>mV@SVVbpZYjGL`d{m!DU--+du+~ zX~Zj$W}D;w;<+mkG8`Bzo8!s`XAjaMrO-^NFyDfo^`^)#DAZ%5^97EwV}?Q(=BEVt z6i67jMDI#JkF#%I6!vF(JEqZDz)}N9*!KG8Lb23jW!>%Pk>K(~4uSjC7vRit; zKtC%&wvxbY`NQn`u%M(XEa~lxib{VjYdoj9F$ZCD8u+Pb`>5jcGA>Az(8@a4Ff|lYdW=nNF8QR z_WdqF&2e-U9f{y~3N>ckx>@4Sz}asE7>QV2-!`t}(dv56UCLbgO3Fi89%QK$flasP zcE)X6AR~&`XKQS1`z@^$7)0J8Y}c1NB^+i$@7{7sC&tQ63&wnS*h7RLRGH=cuFPIe zTvAo&O(%-ijaSD?*(3Jy3^<%9BVdz2HahzEDH+zY#qB@$H-n;b z)_dbhyaK|ku1|U3+o&``1jyLmRja_M$e1J`2IaueDoF(b#r%<^f=R2ukT5Ka;2Z;+ z;qt1@%;ay#&!RcrJvW!w(KlEjO>@f&7W<**qdIoBa|nh=~7Pk+gfb|=G78&Ih2>F zQd=~(eElkdL$4{lo#~k$$Ng65KMraCI8Iq!qD%+|{kaAD8fxCMhol}0k!j+KQSFVT z-x&q9P%c<^g{9%%oFu$+4l|?EC|@03@y*at8TXX*n>r7ctap&?mk(2JJnW&Bm6bha znX{6te_rID?|o&~1JP_wl|)@r^fi20WbsvXJU<8WqR6QbC+~qVx-{T>;QRbf?d}qv zRiVnao~L@vBxzo%Re(c(RgS3mUrj7Kp7}ZE#S%={{VWyPs=@Kh+Xl6zTz;iVF~=Vb z?{s@k?szi5+wp^8J7e=90f$26DDBqIeSCI)??XC92bHRV9OGWM2gKaPmEv>f(es8M zOutUFNya!VRCT3=f_7)bnnSGm9CLP%h##A)uvCI&+1PT-0Ifjq&09nL`y*i0xyyUV z0b?bIheVb!8O&cK%?`&1Ob1ef`O{n}--eJ$o@JAkjI?4{?+gt%e%K;1=VS z#Qg5x@)WZPYmoIdn#`fcvC{VT_9^w(FI*{EIaw7$MhuoKxiW@rXKnEC;x5o)ANYT; zStvW}Eg@vOLM8JatCvH3cT~7V4WB4h3dj2io>3Xa)2k{}wBE1*ZVVG~SZ5376Q$wO zD90zq1aNAi?MyV{r7RCcM0Mn*scg+R#?hG8VYWovxlO^gvXbF9Rqlx=%Ac80D1oJM z*`df>IlCZ(3FzDT-hT)jaVrsV+!8J-k78G7C?Rh0rHC?2R{k3 z)#}d0k>w4F1Rm~l+|lZ}Gk+cJM}$MlF)TW$i{fS~&e3*VRsZs^`^M@JwgAS9opzY?Bp z)Rl{`V1By!3`Pk<1ru`O6VRJu6P?v|7$KF%4)bFZa$Kke3>!j%6*Q$2ehRYQ*PjHk z@ro}P1yl5|e7Cj(q-#gK8ydoUrItW8J`hwvMy1GnV{NdK>ad526xJ~kvm-)*s^f)Q9z{>IdXzrfR2op%Jt)krHjm!I-UGZ+Gz|!js?kR_Eu>ppEGEO1<8H;CKJd@ zMn($2?4hUhwSQKxTFHEaiN1J@*&uNUSJhYmz1GfmGuGBFuo*pn(kF22WvQ_x$@5Uk zb9J1ePE&08aYeFft0Liy;Rj>|8C9+UuPq5d)y>zrZ<#*e&}JBOpr$Gj8$H5(6Boc&?GJ%yq>h?7PZjCRJpo=IZC-StsLo}@BN>Ewr5s) zin_r2L?8qqj0-Z?DBb#bplH^m!D%m*xu^-tcxNn|C#BL#Bb=IL{UF)1Rl6_M;b=H~ z?y44tdV5>-3kvJnEz1T2gKkDvR8$nRb``owv~JkMnqK{p)8f;?$ZY6V7I56HwAHsg zHyD?e-+LrPE8lWwYKy8zu)90zDsLjk@v&8U;)s6h@){1U%0Lq1f*6^P`|%i;$>dC( z)qBcjg3?#32BI`8aLr0;`Se9o6|fkDlEn}ZQd0Rn$T4yTk`W^9qTEZK>B(SEeC5!z z$-*ec1xw{`J$BsBV(iP7->{#%)jx?^8_&W0XG`>#>rRC1s#g zkm-j$H6O5g2%dRaAXwD*_ohzz0FUjmnaj7goh|mmAp%i@Wqfw_KsPSuRC+$&<2`ea z!ue*MH~Z!+#=u0RjzQ_I-E3iYnqQ!ku6ITTi0Bob zy3qqp_b$(_J1sc@k^UpIR;#^)P%OE*&?z>#9$1$m(b7b@(4_`F9?RXf$;MYzS0GQV-&wADJ#0^vIQu4Ru1HyFj zDX|$Aa`cFeKMq9?$cFnXH)`frhD`i2U;ny^f9Llsv_phkfXPS<+3%@Xn>^H{$W0*1 zFmw`E4!ugfLERp=6%4(~P4ljH@v~KBhe>X_;4&&i){_X33_!Z>xA(-J$or3`@Z-Gb zgbW|3N&bw(#kRPRXHUJ}%y;a`P{zPF?uk~qq{YL29+7uE_I}49`YmI#)z8CgVH@Mf z?$PxZ^s1gdEz~wyRVlng-4LfIYj5680-gjF&iUXdA86BdV}>)wM3y_U&vH7 zj>}js;g6cv*Idv3mup--cV4{vk;mt9rnr1pc?9dM8Nx#1V zE!=C5hTx?#HlJnG;jYa%F$g8dSfZXQTke!UuHHBK&I7V3%X;iC`ev|Y(d_2;(iLm} zES~pvpgp-#7-<`l$TqGNm1W`h&=oHusIqfTRN-p5%IYZW6f*rlxhkxgDY)j(?alAN zi8l%=*(&%xpDk1XDBbmY&sG%LGips3-e%~nFw8gHUZowx*--DSbR;|i=oPp2^_N;t z|8K3&9``7}(&Ud0r4qo!r^J18rOVm%E4;vYsE)X(mvKWu=rV|h?<$sFZQf(|wawT? zhYmr3^W(8Vy(X8m(9C7U%C?K&6Vf;|5ggC+M9I{0JJ=@6&^l7xORTY)&2aX(u`A-C zC0$Od9g;Kun0M{C-X~#PSQ*Gq(4Vi_FxJ(F@lhOfhxro+Vk)!LcaW@HrIT(2HSJF* zuz+e+TSNgT=d>a+XeX^?e_m`_scUCK`lC-2>TQruv-g4630~+X1qAF+P3QcetR_dF zmVCyhYtg=vH5R)-Dmht91BJTU+QZZ6O#!n9h|jJkgjJ~Mq|~WWA5@k{dWY+-BXkW5 zR7)g84|rhDHW){dgL2O}O|q&E;I*UF9hOTrBU0x;6bg&E>eab0zU56l6{Ag-yK~I% zZ){N*ifwur#ZF*6wMucY<9IxL;+s8$CiBXVl~w=^n_M=iZ?ei{YbR20u$(uis+1;4 zgStWP$D}Xm(PNs$E;y=T+>n#B2%%(3q)|TvD}8$zZlOcnX65i z8B){psxqQJ3sN$n-Z4Bp{+TTm=j_7pA*JA4P2L826MRkEpg?Wg>bTbA z?`iH}c3$@^d+P;}qnA&AnU(`}om$J)Ew&MPM7R(Cwu$#0+>srZ>3pGPE3n03$H{ky z3xt|#o}~EPH4)ANF}uTY(OA_mBTzaYi1yt5VjeBY%i|cO+cvDU!GC$KG(#p?tAF5N zPphm{RMbmp)SYy1ZsPg^ZD08Kg(RrK?qgi}S`VXcT_Sc^49s*TiMKT}^5?Qo-!Kz;i6n}vC zUMN>FYsWkR%mWnDF$=qvu*t`of?f2fnV0%oQ1oIpaT?6ZmNV{X$EcB&<17n?a#ePz zsBBbOzx5!i{CI2|A(sq+ytAnFN=emGGA3ZuX)^fB`+j>`V_$!i8BNjQiydBLbK(m7sqbEb3N%=&QRWql(- zNXok+V6TL@$$E07CW+fh$mPqG4({*z+vmaVd<|UjFlJ}cR;SsX_G0@aOR9SVI}+e{ z-lhz!cITzhjgsB%jOQ8yF}0aq~RT(3^KuP2~pNYHODx zrl$a%jtI-2aSDGwbED6tk++em4;>?1;-?FCD9?jz3ipR;{D!e2ElKx`DBi0GhfyiB z8NoHTK#P|t9>aLWWKSff?DnDS6cIf4S3qepiS;f%JG#1!0imH%4NgY`WM)uuaXD9( zPsDP07QYU@fWPkQ%p%{nMI6YF*TT_$I&mJ(eEsA|av8qGXV7+{1@dl2{8A+Z`L(9A zXG`|lgcU}xU}sfX(bvZ^Z>Oag0}Fa8W-+en5nH4awz5}x@m|GLx!Q#*tfqY(O(jKP zdS;Dt9@A8r2P`z68^2|EjDn)Y(k{4xax;S@9Bib>LqKjGJr(`>*9=*uYU}}M@gBZQDNf$1cydGU2rr< z#ui%JI=9}R1RUk~+2)USwmH1g9YqBlK=DD`)P#r20S6&~LIRJ=t}Ei!%S27GH=x`Z zKA$4Hy|l#SbL*KJ#TXwvO(x(C)QZwFM1pat6@ODysaQH{jq=|VHJN0bAc|5teyG;o zaQw&3)mby*_UcYPJL>09E79!_!CiP&)T;)eIdNdo4b;d=*cmUV@i?{Jt7o-bfg6`oZ2@jMHUIHu66 zpd&m1+X-Xa7V2O&m|{MxqaNPmvDo4pfJJr-{1*wI$LTKgCkv=Rs|L2FJlrxnn>0JE zKT~P$ygeo%E?(<()@9s0@Jr?B5H$Cus8MrQeBQ`DY^mx1-kvNeX$ZOmbv-aXO!6J$)Nero z`__ZtBZkuTE_?Az{QOOd*^*eKyg*N{LSt?rt69{0RBKscBw!AcEahW}AlH^ivydN3 zAE(Ri)J&D-s7|f>$@XHaA3}zFHJWhiS#YEO(A@4s5fO`INgy?LG0A(COfU%m56V@I zz43rteUr=Am>ZhZNT);u%HAXe#Ma2r?ohtHh_?3j3uH2^+*vXbl75JC+_8E$1h5gn zS(8&K-DK@sI>`9aa=k8J*JNd7`P@99KFV`Yq)25kl^y>947xv+?`pX-7;%{$BT_BC zbC`w}ys9`|#jGU2=e(_aX-&pz_Ov4JK8AT`h^S4;gambRs^e7r;eI99F8CMjJbF(w zb$hDlrchYwacv5z>tD4^W6>g7cZM&PZi$Y2Pr-yLd#-nL-_sFghtawapEs4L<*>tmsL2U< zZ)$2P40c%k)jF5=U|L-xotM{kzJW{eq=&YIlZ$Iu?oZrNM;#?Ef09@$(eA4-9lSlk z&g#qTA;81i*xm+nJDu)MQ_3U)XOe1xeI`&5G!hNAjhI*yz{Bm%F1Iw!(Svg=c#h7^ zrGQ4jfBax4Yjv<$C$lc;&H<*|lI{e`PMMd(N*HeCh@l_ROkCM)r9OyT(1I5S01sc4 znKBPe@{Rg}HoK2bCiR-VuGnv~m{=@yh8@YZdq3X6_%jf02-AqpiFswXU*?5rCn0A4pdzyWU~N7Q=^GO z4H6y~BV3hq*5-)W0_PEjnY3EOjuE*rz;5<_96tV1dR*7m6VU5}UG+DU@4@9$G z)!-N;WkyL)ZvnvYuC6W)%ZU)OY#=}|rtoTafM~m(XCdlK=Blunt5c2x0_zbv`t^E} ztuiJze`;zfFBjKaxgM0_;^LlfWK0GR5YJuqW~u;i%0HZpJm)#xNb`Yob}CHj=E?s^ zPk$MswE_w47MJTDt35*086D7%)Oi{gS`^MaHDC8b#Zt7Dk;8KeMO+N}ycZVk6x<3U z_}MrT#V?1kv0$IO^BkVTIZl{cKIkiDz3(|DoD3us3IL=ea2`T1CZ8Y4CJ*5%1gdjQsk!eA)RGxOuk zVc(LCkG8=dXUrvo5{r-Z6+*R~X6IUgPC< z0SpEMTzg#%Bj1NP+iJReY|&+BLgpe)bZQaajG#nF#?Y+w#=(J;0Eb6MN?e=gZxNW? zpC6({QyKf2kLKXesehNtkAjUCs6{9be?n~JIu%Hs@R*3-WS}{(0)FwiDL0r#RbK0jxR_Ol@6QzMabN#cefJ3Lw^SePZ~`9_uPZQ2Ah_2QFC#r(T~}*51av2jXU%J`k6=bLw1JU z(zgJh_|nQ1>rIIXreJ!uiMJzE0@I**Ku+#bsxEzuJ=`Pkb%V~g@M8X#AH z5k4Sv<*rwJ$D3C$g>WbP5?Fv_zO}wS0c=}Pu}JfO_Ww zXDh=JX&gHZ8;cTmZ4XMJJ}jL{_e!7rCH;DI#Y4OnJ1!Q^PM!pZhymgXt}3;6C)X<(KPVPLs@#j zL=)Vv31#-nXf%Td0kJWg-)dSj;6vtPepM06gS)-`KLpWv@?rBkHad?-tktD9 zBvWhrU-i%I5EWe$Wo=1d_YM8_!t3Vb8W7Ve+;()C{Nm{|;|a30OJn6k6&{u=*#+HJ zO9NCC-<*}y9^ss`m9bLdC{C6lxJItaBKiMXAsx1WVu@($?2tjrb4?c_lmT$aDyYFbVlc0bL* z`DFc>1Nkp~RuW4h6heU!_jk?q|MnV5B#zc;aOl;klKrUKuGw_EzyMl7{bF|b=psXMK8*eRpAhRij8^$4>EF)9H^ zcz3Ev7J~)7;KmvQ+edy3!Nk#!IF54zD5?>UnH<#&09?t*#E|$32;!h`yh} z!EtZ5R#&soH_+t6j@CT(md6Foqcf|-NekBO_6A0Mg?Xi79diALb<3WP_f#2Fxd^)H zz7aLa35ddIs*i7)vy^cTdM@^` ztuY|2o;ObXA4sVGRc)0;HQCP+xtBX|0P(tMbp#HlUfrYzrJ#oCG#HF-E&dAJe9+4Q zQ&dj6otTAR?(NGp-M(bHkli5jsNNY$s2|T$;ox0=F62kv-l)99zmlt{^3cDlORsLc z!FU$dcp1Dga!A127yJe*xJiBQNRv00Q(t~5Qh7k})Oo z-tVz+;I|uI?ljoQ*%zVEk6RK!_x!{riWa^@e~Ys4%v+3}w+YuKFWF8v`968eL@Ijc zn#Qje%`mM@m)1V9Rj%{g^S*e3V_t||%%VcFJyb?}BT1>p7lhFIMP&-6X{OXA!7eoV|~Im1X)eV(RS{?V3WFyTb= z?7AuZZ1+vU88Ij6b6jj(f;d16dtR5)DHXkH(6Fc5?Y!6=cx_=C$bCp3W~HUeoZYygeGa&+T#Rd$#;Tb@X3qPc~Iz#6B)A!#FnUrjNj!_ zTHf{7&rP6^sWDx9MfRjnH`4`>rwSPye^X!PDRqrkyu5P%puWbMUSvHv(#3R=SKeu#wV}t831G~%o|v8OJ*pbTX7y~S zBE63xuI+zB>4^gllY_sh(j@=6Jxor(-e!5g*y~jTo2h^gEoJ&r$pU%`5$=oU2`8Z* z)8(W`csGQggY}aIL`buflm;2qz=zU7iBH)%HFEz z1x;M=s&7$dtl2H~%z0ku8X6XfYy4QT%NqGQ%moMaJ9XIl}^fnp2}K;)e>@dtg^H zSo%y-%ssS7vypNy>5vBoj=xY z0aABY#2si}k)3=qFK-WKo1l`KN7vS#+cdbS1TD0t)H&tmLV;?XEn+f~0E+~xSUsQF zn&-Fn8{q*6`aEGkRuPwO8qGXI+KL5V^Yt{BIZp~@iyU{G$&N2)^8d z;A*A2dSRYLeTxgxSrt)@Qo-4FK?&e{{PsO`#$M8?{54Kn)aefactBFU9W}-}-MK%hlXF|Phk-b)OJM4|EpnPaT5!*gbIc@~*b2`R zQSe)vO7VXh3tcVy{qL%QaV-rFc!r_$2ZT@SV>pgUIG{(Ie|S^{&zO5<)RyFn0TXEa zj7P7kU76$ufKG_DjYp0zxcUVUe9YB9_YzRRGm7dIx!qIi)mo2PVH$pOI0sGYCNb21~;b&Rzz0y`cVJmdMdPkbyw z(Bfi98f&I&P$W<&*lUJ6T+Da@uQ00N@GHa4!J$z;Z%$MGh>+}lrB3mGG56L{Rqb8h zw+aHHf(VF|fJjT1w1A*=BaKRf(w!n8f`B01-K8{aLP06%losh0L^|HNLCY$Hiu|*IK{$&d>bLxoB9>l}mU3{`U=#q-tlJ&zF<`ExmttmQpo?2cYmf zuITOxhD3Ra--wq@7 zm@nEw&P#Q_$R=+lQGbVrJP<$t^tb5w?Hx?>kIGl{gKn$&Uz0vBJlp&t-57bGx~n4B zfaSU}3T3#02d;jIlG-gl0|PIq)-3oDCYjGiB$>l8>TI)pr^$NLP;x!jJbGmQUP7vy zLqf@5WsAthG#gv9Y=ELvBA8WBHGJTEv0qxcaZFlvk-bacA1A=@t>)`wxsqnS(57w zKgswnKyIH)8!_$@U``vVi5^dMUYvhwfZrr#5J)Iuz65~DD0`h?DhBY7$Rzq<9?=uvJ z`4*>2hJ+00{>6B`u6S^cBR`+Tb*gm@mXkqo+BR%D#CF%-M>2CVcS~{}7=B$B(To!!3Yl z+aEkpu*n1u4!0)&3lCM=7#T+D+|lfZ-=(FkUH){aK7jIm5Ws-PzsW2rG70T@Sy_pQ z!=&dHD{F{5MIiE(gnQ zWjMTbxg(x$Q!}-CkR!dgH}nR6TF*q(XU$VTK^yhy)qA^|Z(xzq!GTNvm+c^=1WzWE zBiz=4(FNBoBU=7Xa-LL?YpjBXifWy1tZshZn5)+Lm+3-J`mYtqaAw^EkAW}g_*u8% zHuwz*@bllic@rS34dTut`iBK^J&E;0UxT+K?d~P;i#j^q=}f{4cW-%1HKgV|8K#e< zk*^7`FI}1z*U(f}#v|^f3yVzBitV7XB%29C+id&LxVaR`ZK1<8@Xw2JTPfp`%cqpN z@+Y7Dt@s3VyL)j2Zd+XpbsSVyyrFnUF!LP3asB4y`O4R1DIb3=4VwIz?@opCVcJ6# zn3sPP?oAWA7-|#xg>j87>e?(=YT!pT($D=B82niT28y`X#lUm45nSI*{~wQnL2uwb zpsbxR=US3%=b7+_;3%HYtktHYr=+J2S74A=f0g4Y*;>MJKI z(5(ufUq?sBOXY4cR>Wp~i|5|j!C3HWY+?LnMjiZ$| zuYXCWpet_#`1vX9)CLcX;bi8B!c=wM+g$kBSI951Rw_%+_^+q9D9xVyuP3Kq6m#Jp zr-iWAc>m-xWKPn3B)$|VLn`SLc0%d7&mw&}XxP|qkYE=J8=K&8XuG~L;Gja|{&Y*{ zgKS4MqW>V^!IhD6Cy@o8UIk})VCM(ZEuobPbGQzxKehI+X5o1J_;!|9x6)edByZp0 zPUUuhl5t5M=9^^Pwd49i3B`4R3cA4mhMcnHnAh^{6RDsu7Uw8;t z!|jB5t-LFhzUTJMC{MRe@Yvr8mU1^YH%OdG4}Q&4XTNc?#?qdHhbQ`mcH;eN)+_W&ezE!Ze>_|*+G4owd;ggBw3%9!RR1jQW=j8fM509BVj=|NM;r4)cB|Qqa7e_ zjtbMyD2OiVhSO=5-Hs0)5!?VpuYp`Sk0)O7YGOLhjj9G})uC++u>^VZkjbs{U-HOx-&7#fIPlr2J}mk)4<6iGmLG z)4feinKH3li+UNYL_8EW`X3cb9?>tg5FuxNFi+7VIt`ZaU;2Sa_|fc`Y~_2@{dX=* zaKtP_L22+2&~HXj0`PI23d>1)m7I4T>H0Y8lr+Dkseynx@}uFh-M@BQ4Ef4D1=vL{ z5JKmHEBa@<87j^z82KjU1%qYcA8v-c!Zzbki!qneQqMCi=qECJJ@mTGm4CI^UeW08jsHYFg$ZonuDy}85f=^Xne$yg(m)1!Gl=XYzT7g zY>R7;AZB=e7^2Z_UibCHWDtv;!<+g){?{lBDR(n|CZ>D}mi9{2=8=~O=)uGr>+wOl z3}_6d;&^PXF{)O#l((Y386y?|ng7;FSzCP9(xy+Z2QPwhBOUWQ;nrwW{w)fJBNJuh<{7>|_OVALcb%^Rcq2bhGWK*a+>G zQa(9!to7UKtz-EQx&h2CqHlo0#(!uou-cc7D7Hxk=9SSFmAS!9`)Qeuo9ysw4R-#= z{(Owf#;u3p4_g~{{Yo0W=jPWh*O}(=P9vU8Ada1cfCO16yV`U!=SH!fBH)r?h)P}i zvle#KmiYa*(xjuUVgFg}D@E51qmLaBKgr{?d}(*2h;M^Xb>ti3HP6#ArzCf;!cTVv z<)2J%Ny?cD0w7Ep#dUvGFoU@EY+ik}n7*G~B#F}6R_)vA{F&awAc3_bryDODMnp~+%j&k>KCopCAW!&GL2i6vNR zb7;YpizAm3Cn?ZgZT}qU8321VrG9Hz1(vPlb#1H(Yx40 zy#5HfLgjm44hUt+v3yL>p9~P-u*y`lHPDx)Q=DtR0H3|~*A~K^?;x~L2+1{en-W%d zZD$ph=`n~d5w51UW|S&uL>309Bo|4sqmx|BQCm13;=%rrlasebv*~>olkpZsT2CW^ zj!wJ<8w!Rub<(+pODN|=#P4ck`74TqypjyP5&Zs)JsrKGIF3ABPlzq~`3JstjVW&3 ztu@$c+p5^+@s%?FQn`DaI1y^JW;du~v}QBNW3&<*ThdzT9BIx}a+q}Zl3$*))-Utn zucTSMDPPYVt5NUl&9V$5{H_lD4@t>wjyJbljf<}lR+fv1#42eTRpf+J+4|E`KXa$` zi3$i*Q5YbVd1 zje(8gaeSssD7l94*?2|#7E4KJa$v-fr3$58Xs>m)@cxIGQqv0^`nFbf2G6QmcXkO_ zX6_OTjn^kI#3O7cHd|B;`!quMk$L%26n)O3;q|+!l(~VjK1`#9qZOTqQZ22Vh-}EG zg3{bnnkevo8&#yrVtRSHH<$|?-Lm$r%MwJReeQKgFzNtf-ZiLO+Vr9rrZH2DP)^8dc|=eJnKh+ zz)pFC!SUWNVp`@mbj+R8y7jAFtgeeC>U--v8|R~4@^^NkZInx0oSj$rEJZArh7j0< zCQ>p!e8(zyDnDWchhpqzu;P7<_BJ1OL=>jjM6R|LzASz@;Mb#3Yn|71P4<=PbfLp) z`f!XrK}loNQD*xaU%4+RDxFa$Q};!zswO&u|I#A2@dK%EV;4d*YFMmque1gwNbW67 zps`J1`^s`!bo%JnKhG0a<-5^rs$bg8Dn04GyH~?az=-{9 zp#F3JFOTCjzc+&WiW3DS2{yl&3^LyolBv9Z9huK+l~hFWYA72c2o{C#juNl5ccUG| zKM8KzJ8bW?A6rj0cZXai;<}mj(c|G~(h_;zcNdiNHNppawhK|zv>7V8v0^31HJF(4 zM@kM&o3`6_7G^E7$P098s&sKZG%BpiE=CrZ4s!%w6G0Ho2(*8(X6sO!P9?M;Ergi?4Q$ur6`;wD&bMC`CW84VQ`G9UCmX5Pw{LtpqCHEUGa?W@Qoe!`LWio?;h4DOI!SBU||sJ;eeAME^K4 zk7X`Ay>sU44IcJ(;UMhiDpJO+WeVi0 z1F(d*g5$t5EAC6abkPSKtaq##jgbec%Btj`&KVQ zBO`25-oB9$1h{{+t88~y5o;B(Ic?DmsgjkJ`4}2ozLM9Qu|B4|wlFEyctyf5QXb4Q z<t7I52FS>~AZ3)aL6v|eN#`mPzDTR0_vaHScjo7G+g@n+g}_E~RH?ll!YtPX zA*TbE|La!*nLP4AJSsu#a7@3f)#o01;y)dl`qW`*;2C&EczAdQ3w4jTMl2e@peL2X z29_22d>)2$1CK0v!!pb4@sD3<`sH($R5)Xp4wd$@K0;}hPETaV-Tp}Bzziw7ik$av zA8_KIyl76f-?(bCcSpes@Mv`!bG&B`b2gDom@dMeuB`k<49E-#9Tt|Gj7 z|74E1ukvTc(QN2;;VdL11TMP8@j4G^ps;)P>{+l=fq}E!eC&z2hJCuEq-07;%E94b zO-;>-=@hmHIK3o5WkFBQI_2i1oM5?1aGE8IpR7(^ z#uSu}WY%5nNxztW(;&$D4r!K7jdN{n?ZL_v^9602CmWO2SCY+U;qB-U;gUJoYJ%A> z-^!I9Fh%WBZ@esG*kOrI@I0VOHj|YW)WW)H+s;pMKxI&_C~3Dj#0Vwng{CwF(FbfY8})byR(e08KF>^f;X@ zm*R7m@|!gy27*T5$r z=*@s~^0JnemW{jpJ2Qd}vr@mXbl|KNwF@7`@!BoE!l5kCt&??0P8RiL(}SD+`Whn= z<5oM_m@*vxlFRNu@R;^xtxq-SBJ2a9dGb}xEUEjM0*Xrm`Qb+y(&I#|aFKW$C2#gC z-%eB(%}&t#QtQX7O{|~9`6R|k#5N%O-syE#6ey2y!?mm8YQAgsiQ}jD{B=@A{3=U1 zIk`D)=VS0ysqxX!WCBm$vriWh5qYAgcRqFCuEEWXm*j@nSXe=C-WWae?_9C=qNAmK zuprl=DvwTpAaac5(AUEqzy6v4 ziZW~$zA)Fd-LC&|T#b8-&mOUVDVDjP&v7@6NK&mfhn1ZC`HZNrurRm-E91FXN&=I$ zua&c0Xz=h5_Xyvh^3lsqOq>9=nk`Ix3$Db*#zvOaTt~b{sVUu|56r6HC&mCa==ZLQ zm(!ii8TYA=Vw&nA;fGUI*t-7pYSqoIucf<6Od5w@DiN1vmwOe|O`!ss8a!h^3SPgK z>St5QR{T6P6zvYuC50nJ|7w5+O1K{_2P$2Iq9_az%p?9vpYT@2_`BxjOwTOAbKYF_ zXIKOox2nw$(uUu(jK1y!&=0`O=JvLnw6y*;9)U3J4C}BBZ$tZdI73>50tL0;w6D-1&%w46rfZ9*>H-|NTX)%iGD?pr0U{dFGKd2Q!m8YXxg zIelchG+24YCvt~|X;TR%fJ}~R{&0=Uc36aB%bbE9W~k9s*EW9OPDZu=S$40f=a+;@ z3K}L{q3&nbl}L;6p9wXk?YDWIr>Y@IMaC|N5xN-d@PIJGQn&1>I$+jp>1uG^cBkG? zblXyQLs;)D71oUdObQFbn;EPiu>Q{Pm#G2`BNxdj>irtLm09UaW2 zcILW+o7l<%Ce#U!^DDC(ITZd}lu;tE^3w!5AmJ5Q&>`PMO+$lCawnKjE>UnSM}>hm zbTf2#v{ISK{JT0m<9^T-a9d1F%naH1R^6U;i-Ei$$n%8%zqwd-V3r2Js8M1(xwyFZ z30KbbSO3F3`CGCO##fvb8XQ| zkbp|B8>N0WROIv4o0##cQWSq^7w1oJmJ4H&RDUg?`lXNXs*po0Su^`@BkWp$$Y2|8 z{ViKkHT*0~-xB>#wjAH%+CvsetC4F>wS_84H|c0o)BTbd8naKg%jsuL%A-#gA{AD- zLQdrB^CjQkY4{oS>9100>op66C8y0g_ep|1!vzql`V|2jqu%Y2$oDLIM7y+*9;s7h zJ=1!y`x929+qJ4X>Xg*K1tUO7N|byYB$SjEyZ92IWQga$s|)`Smjk^$=~7UZvK?7> z;By%MR!l!I>bDFH11gGK?;t79g3oxp)wxI#HVQR`FjqQ7jJ@+b$QSsZF416 zG+5x45qJT*A+=v5Hksh@^LPKE4rdFWo(V zRvr2a>2tF1(vF^ecgFELSX}>cgvnm87U^HSbl4cuJs@#-;a8+%YHViI)SD^no0O^~4d8+~fG9FvH;of_pt;I=?(=(G zlMdSY@hXFf=|?$tG%jhuH{Wzo!1>vrjsHD0s`aeNwUJ;$W&!rs_%+HBTdnpD=0Cd%35y?;As`Lag5|5o`#V*|JG%-w7! ztO7j7C~bbq|vszEZu*uoEo6xGC6u6#N)Ig<1k_I$^sol;Vx ziiIBJ9exAt)6CK~(f-b?4ns!Gd_>_X8?%d_7_&ax9NoU)M;yH9aoZAv+{q_ujsu;b_?* zLWqB$3QQEY6Yco3DRilG9wx?Wk{-3RS6+;)sb0K*^}+M+bLzu~Z=6elNp6pIge6+j zT@xyR+W{U6D{N(QSUuhr{+UA15$L{Tfs&vX5!^L|9l@uGkeH(qX3< zg3tv>nDx=F0XA{MRvgzevZjWlo)>x0>ZOS2I_W;%Dlj*TJ$;M3~wJfxuTxb-1=qs8D z%gW|t=E(fbZZn%^>+G9ic!(~nJjPR zZL*L%3ye85l?xT>X(r48 zBvP^cuukn*C>!0Y8KA2kS=iyI+Lv=TL1XWTKAxR8X1&A1>3g^rZvVv<>%-*VZ-pGU zhbX7Ruf_hun4P*xV;;FtxU+N@O$vu#UK|UtoZ=^WYHl;FN~?(pq37R@b!Inyx!^ID zs9%IOLC1%6G=h7}QtqGRxGap0n`-*Wj^=6t6CsSqUao89ct$k8upqXNest)VAFZ#= zM8d=Sb_)%ab7$>cg_kM8$&A*YBT#s$^kjQsdm9MejD2hRL&G*JyL=g9u_HREw!Oe# z#2lMHe&D@T-%5*T92;7g#JP^Q(cwbI3aeOXhV1q)GZB-6f$#WjQ7-w8pI%SVQ5zf; zyB)t2a?DYN{b9;84^6yD(8uLGIzE@9GF;;`AA<_yUg$XuHt;i6v`u)={62@9>FtZ_hA3yD z+&~MSx}biUCn2~$xZ9_GOdLs#WZp2nMtz@0|1E6!rkT*CxYk=5E4R1^odk2ln=3?A zZ4i&V0~qu<%P)#I(-s*`-T>ne5fQxxBYi@txw$#iv0b=uA*E@JDy`@|`^e4Y+gCpo z!$#kZ8S$GL&VE8d(WqPldQ!V6P9U(fpz1pj zTXmiuxVw2eHN3R6NJs(E4Z>xcsTh)l+iMdOU%q@98>@kgGvExvEeMXYvS>X#Ja|bi zPYbI}?W|7;3JO9CGzP}leo(@6=g!l}txoCJ4=FX`RcX9ViGD-M7lB1ULQ-wmPW0F@ zJv@*ME$jvWVAJ6ex|(Ew3kHo~s{xt@iEUqZ#aKd8AxDxeD3v!#_uUg;f*zQf+Q#8CLWFh={@~$3FK72puW;IKkdlr;Km{&7z*uhx z*DgpM(9b9s4r zS66AP7+Bn1Y61Xsg11xY^rxgXPhO0&RpEDib$%IWCRtyGfDOje>}H z3A=OY2Ik5-#anRMw6(O{>iC-TRTTUg|?EBWwriYOjlReFwN7} zD6=u1&tV6i)+yD>RNRFkZlVy_atnKq?a!1`ZZ;*Q)W;agv|Q`Y9rPu0Zyt{2dbT)* z;5JjD7I6IW9C1Ng(m>L9U^ZG?P3-~ILcV4>zkq;A;lj*}n2O=uyJv>fQD^1GsH7rn zm%oPC`Gn1AGxCKlqDoS6_b+({vARnIB}h0z|snk1#uhOT)@LBam|XV(kdDPC49Un_K@xl{_7-{v zh>MG7Z_;5oTzMhye6iepcT733@+=9h^$UsnJDMdx4F4lU0n@vKFid*#|_kS30m^@dcyi81A zXQM*T6K48JADm;qxkD*Nu$51q;akePjrc_|5d8@%nR zv6++A*Vj*FQ}8<8TL6SQy$b+Pet&bJw@=VvmBgiGq0ZXMDj73e$6)L0*RSv1u{?Y@ z7y-|r7_A5)d&Pwh{$LHbzs53}CJyxbg>0Ulo;o@@%CCMhmoI;P@0IfdIw$P!@AtZU zVbd`&WxO8k>?}NG75o@x@|<$)9xJ7Tl3xswV-ip&rYuzNP~BlSkRUnj#Khw`3D4W(bUJ;h z$;W3-HhtvoPpD0cbmOP+Rh-B9d!T;fE#$5~&0o=X8%nY4tlK^9^>^kG)=A*K0i9RK zo6tXOHAkCZS-YNj`UaW1f&;@zmgpbOp=xfrgdz|BA2Vhn=&)Z78S`p1lrC%V=LmHn zvus-y4h9JAN1-8esivrn=hfR+iiB8bn@!Cl4?QW~f#A4~cNLF>euNRDb9lIPjMw&( zkSo8=7S4fP)iv+?OcN9(;wD3-j>;ZMK9_Hx6?IFu1!VJT^b#x3yic zyt&MTmyeE;4sz1JWk)Bdu-I%JfLxDsQ$AEdT>I44{MRQthmAr?iToIUeMJS);wu*5 zA5%_ly1LYGynB;dk(yESSJ0cn#Ny)O(e~3Ao0x0Eyz3nfXI8B4*84*+SoFP;s=a5v zZp5DD1|i0Y;vldw@5@tpP4;TUX;6ZqGPaQQ>@4BWj+e8H8kH4Qg6mE1Q0{loda)P% ze64exXP~#2frZ7fdnDIMdi*QIMFY=<2du432C8SO9E9{6Phmo$cz^y9GS6?+`*SDD zN9y5uR8(+oyFBi@gUNIc;d(e8kTrO4h{Dg$pGbVwif%jOh~s(m*PX|~Aa5aKE^`p8 z$R%F)-kUH~v53#d!Nw+6R4BV0ok{zB#75ftH){|DTcs+trnbG-nO_?8W^+1v@mYhl zvbZQ$4;Ae^XtLj5Uw#nu;R~-;=4+(Zz6Cl1-?DT3L}vip$bs1Wsq#JzU){mtQ??)y z(n@`&OPpDk(&6b238nV~qd(ky_nx4ruvd@*Tmnw|16Qgv>|ZXTMrjsi?kD)mrSI+h ziQD~IpY4$lNg*zBsR}-!Tx%qA-4Pp@sZW%O>%1wCTI2u{IZc)Cd5 zxnUF~-i1&45gpo9WPH%DBtpu;BR>j8Pr!#H@?5(=U#|e}@Nj6zyxotHLpd?BLkOE~ ze30Cfxd=Bo`Fz*=I|G82)6$O~v2Y5G6nTYLY9qGOa#Na=KgHuL8d-j=Y`>(hB83}hi?9TrR)?$qlq_#=b|iy{3_ z_Br*$iF3gWM*T;3oDkhIoXizLGcy^KC_fOG03Z(BrU85Rxa!! zWrPT7(W#8c=G6M~XBwSODNi6+<^s<7hlA7uLF#qyUEYj?ShAgIbX%pir3XoG2kk_^W}vo97IAv8W2( zd`1s}>fo1L9eGRI74fKbBy=#0kC;`?fXagng+2D?!sKDUA1}K(~i=w1`scC;4UJBE`WQ2Q>soS@)^bw_E zU6)iaf%L87jRy|H%?P8Ex9SwSm}%y>=rSP7pZIy~YvckCa*N&TH4J7yPUHdrXifGp zn&~q{suo|HW8Y{LuX_C?D4fO*10&DFpzqzrk{i;{mqD?=tdktCli7!UNI&d_+^g2s`*_onrLaRjU zRdBR}%STO%a`WB@JAhuRFo&|kga{*%SfNt$aPM;1U{qIs%=3Yg@U2K4Kg_jZFAoOo zt%V^(XDteiv6-xo3(T;#2lxjWa%G0vl{PoG4%prH81Andm*^?Dk0fv!zqiGU;a`yh zRv}{6mx#uK3v$8mUX8d*A6U&=Oe#ZlV?G9;pkouWyIX6_mMZ8`|B#aAk;hQ16Bcc~ zoG#XZ-X9h4E!a?ZZJ1`~3(j*ZVO2A+KNsLUAKt2mV_s>nXrJ4%$MCcioOgB>DGzUv zaDzC6{e_7^@v_vNWY~!rTVT0S*auSSgqi`i73I9%+%|XMwlqiA;TcoIc}`!V9Jqd< zbZ69$jfv*wa-LlXwV8s)%#%vo6IRYQVOUXiy{FSsbr&u?gAmZJj{2yCSRqKG!d9g>!xQfZFiYCwsz^>I-MafnK=QhXj<=5! zcIJzjfBnAcKtX6^;#XnyR9w&h%n-%8?m7?}U)5KzA}+K`rcH~EDQsLWjnE?Xbq$OP zxJ>pe<6e)X`?oXXxurMHOufh(@IMpqf)vvMY`w(%m*|h&2CjsnW_aawa5vxbTp-W= zd6`Wj={$V@_VZFQG_a-o;f^PJ^tZ?GbN@KfMLPPTdHw7=0VF^mAv-fd3D6#SfUAsl zRO+wtJ-HLX@b;uz#outlUp|%r0!(Ci-aJ;vk?e#kXbqJs6uA^lxJ5*4r`Z8--?P(> zBC61KL!VRMHrcZL&3p`j%$u9}li)%Y$Pr4T0`TxvDy3icyArn;~ejB0LAhkS9s$X9?j zWc6^52)n9n_bclthz73Hz0e^x_6+Nacc|1ph1rUv>89RK$@NQb|LZ?)GXYnC+{sgg z3{hV!ze06c{y@mML)3b>Gxg^9$WB3ISe^M|dq5s6iv?6t`Q@W1q~hTRkCQAov`>1=G$gS&v3XGHTMgw~lEETHY*GCls>29$T|d}1c8sx}qBkp{fD%GNLWIquqoey|A3S)JIK$984{gj~@p|%%qDF07Xz)q&w0U+T zYSbLmcWIWI&MYnxLBvf=yg%`wZeeu{q1P1jYhwcw3rnZi@ba4$VyId$87a$$Of?`0 zp}l!ZLupyr)rmTxKnOY31`8*k(rY&hTFaoHKM!A_ahesL)+_ttqr=^wbLgn3j?T{F z_wJ2W*}Wbr&G>lBR>jK}EHe;#wLON0IS}xx#$~p;bPw-5mRD3{GaJZ*k_SaD5XWJJ zHrCej$mk#IFpZ17=Y!qSfI`B~-rfec?46tJZ2M3-7sFv95?!s|ay36{RDDQ=Q?cU` zfTD(mRJa@J>gsJfP~XrSmW6|h`?HO`lO=T^>?=gP`s@_BDuK z>Ga|C6Vici`I~g4rldg4QL8vuHJqSyLm!MAS_pf_QvpInSV|)ug%lvjss}d_u($jc zAk_UASjubN_A}Dc6&5>VxeFomYE_VihM~#tOAwFrW+=JLN0A-&AfYKCElmRbg4oN< z$DARg^zrdIfByUwlqy1(BeKsaAz7>rt6CI9+AZLV`iIY?ep#dU(bcLPVF=5_G{e4s ze^N!(E$dZwZR3L^Ei9%fsi|N@TF6iS8}dkmk{duCCEZWNi5^&tB1^omr583eG3cm^ zp)F7k8@Vgk%LBKnFSX*zsQwfWSuY_2R|%sVQ|nE-tP+ zccO@powy9l;nC>}IZ2mqLMPMf@5~07u6cfGZ(jip_p~BdryAr7m>x``HSO)W%#{2Q z6&3Hw6#vK<2GzU@C|6r*O<`#5?0i#dg7@Yc0YPTm1+%WtjqW%K8h_-Am&Cm3`Q)4K z>)$^E8FSm6iHsJzu6Ds-%2)Wo0}KzcN>^7`Vz!h zU(jUp`+uOxD1Kw>{WKXdm@|-ZB_JY_MBo_m+b>^#Heg~RYmlQ+N>7$9ggeZy%%}uK zM!+-|Xo}5bPR@R)?3pG^-owK~TwL5}F}s#n|6ZZ?CLl_=({f}^?@E>l=BB4da zscK89{w~S+gsr8)Ko6W>GY1siFN67q3eCd)?OY)!hgk}0YipwISf$TEx8>@%U2^U( z&ut|+fnU(V%N~Yx$ujZRzSW^(8W#ydPk)yq^*h>9$`i%*a9WLp7?xgUTgh3k!>ZbOmTj*xcD!2j~*km+(|aj+nRnp|GfBrl-FzQ#_dm z8p6iM#qNiDuonHCXu3d?bF~#z6B3u{-rC*S0Z0%gnfTojodToAtUh1Yk`{(IAeZ#? z9z}YwB&k_=H_3~=U0{>CM7ROt_M*L$WV)hsvmrp33pm03FRuZFc=bGwD;Q)I5L10X zh>{okJ(k~ac{^yI(wNT>E1l37S^P8v0@-CR8`*8qv{3E+i<(cAc-O8mX_n=IUn_3} z@E;IR1-itTm_3*-*5yzjM_b663<3iwDijnHpswxPgk+=}05O}5X^G(&rO6Ht1u^^s zDC`4EiK3$7-u7A{$>-GWAhF$NE(1xQdlZZ}N`!cvH)-=j{;agiBjs-}ovSW}g@pn5 zoCRV^7cQ$z6$W}9h-#*Qsk5`Qj?Q=hA$yb+)Yn3fHzr!zC?L3XU@P_^zf8=PR<2@# zLxLO+oMr=0VNsWq%Eo&Pof=`ANEa}&w%&m1-RN~7F7Qm7l!N5NKGnT1O7!7RM7VH& zU$PS5St6+VrJnk}^);}Di(n~4N`6pW&?cx-?gJLaMURWrWE}|tQVDr4(neFZ04;x& zIf68Sko~C^9Q<03k;p@?d{C5SJr(1bx=@H=)Og<>V^69;AtxM*sCo`?A(Sx)2f@-pmKr!eO} zL93_8m0`wSriV?*%9PZ-fSYJTyeUK?%Uofj&Ve2L!Gq=|GLN=^&Fd~uY?!@1?y~k? zH_WQ#KbLL(WJ%==?Gl5{38olD^SZv5ucuOaHI3@9&qGv--2602#1DVaCE>~@1m-Tu zmcHZ{n*ont6xBes=5OGpzTu2D|3CHB|6BJ3M%nx}^$gP0{137CU-GdW2TT!*Q%mj) zKdG*XDAV)}ngqAO7r_^GvV$b&&b_c-;UOQn?0de`kMc2GpQjwS?0WRR7o{G(_2tLw zSI7~6gc96eG);jmW*3%&^YskKIHzV&1<-V;*P6_I_*CA5@V0zs7 z)e7v_*YPNnWs6p4dpnvx*ne7nINgl92EccU5JC%OK{PS9e`|m6DOS} zuC+mB{aPqic%`9fYRG7}m7^-kCKXcV2X{w15XY6*zT9&7$w~czUvGSsPX4b9ard;HN@)&IFPAJ0AYOQI>#96O5mXj-d{yYg7&-%iJ2D_k} zZGqg%4OELwgM92VitwhUZT8&RRK&TnX<9{pX%sp>bfm%KW+q{g6!nj=;&gX zoas+bsfT&LHgX2PHf_IthNw`Wn^Bk*W8Q?+Cl8Cq$0vU@SkKWRnb+8uz|lb@)`dB? z==VSUp~i9Gz$As>InX6cvhgwlF;*d7!}>}S3M6=qEvf2Z|VPydd7CKJt+BA9UC z$#(irr|}Fk5qAey1zG~Z`X2q<0979ve-ggn#TyIsk8R(Wm^u73FN$kesW;QnzPb_!a5k*!J>TF9{5G4HpZ z`F!fA1;9bK4sGM^&pr+ZK_O~)u8ncs^%_X3L2E08ptqt6pA-rrnXYer@jfVO=Xf@> z`vbVxJFd?PvPa38uPd#{3?7={bI23kzm9bs)<0ASstly_hRtB}Qs|bTdtiUfb63d$ z_#)JgOouqB8Jyhy=gS!}2+36B%*eJa(8z$g+dJSf>700ZyzPL;p~bW7o4_>N=oY5U zh`4g!&hA4aHp)zFeKy!(JNgm31{3BfW&H9OH*KTEcBC%#1FbvNL#Wio_azhV%TX<0 z53-&ket!DlFQ1O)E3jAt5Y7D`dI-ll(p;|HeI>9%yGjY6bMMW1Gz z;FdV(O<>JooMfCdxni!K<&EFXKkK(&_Hu-Dki^FSlGGAlZOTd%mchffnb$2P4cIC^ClNr{}_==$A+~-pnNs zEN_Huq(^-)gu6gVo_iV2fb$%3A|_iV);(HJ`q%({0A8Ntn?TX=&CEm}NR5#4zLbmS zdOb9uR*?r@v_T!+dn-=e7qbs%Q)WcG+p0${jyoglV$1T7(+y^wRQf?nMOdb@z?ZQ_ zu&q=#dKV0!mSp7Hk;Yv2rIt45IGahB z`NM}U;!sP+Y{6nj7rF7~pS4!>+ZwD;Y}KQi@RkJk>m~hCQxGc2!m7FAcU6fo{+lDM zRduyjY2C55)rYkk!}N!)5Gh~0bb`xbrv>18GmVcIt#q=OU#@ znCtU2$2R9@?owb-VXs)*os0u6*OKTWk?S{YO*$niHK3;KLsGEA`2H)x1=cdt6_-9W z!ki@*Nf#td6n9yI+w%qML!;XM<1^1!{#bsnFdamTz_8@zdI*(pI|n~P4wz*;N&kQ1 zb1_J)A8Tom;1H<#ZB2RcloF{_gjlcS+h0A-#LTh(h(Y)e5>5mG$Q0!)@}FX~qm0>@ zcc_}Vias{n<%niv*UQ0r$oXH{bRb!2;F}>Ifg)DOuFF#Z`TuRXco_^Cr++XMh;|IN zrl+UD*6iZsREa=rFAc7NJ-kgvQSmB1{><9i+RBOyiqn;WJMIS+Z{Bo*G|tk73LWQ7 zWBQcEh#$@82Y|N{ZGSo)^6O@#9BdpK_J3Tr5{0A+)ELMRb`B7KlMy za_=4s<>fedLNOAQOi2z7T2Q1#wTV1z2gAwFjg9$LUGN<6F(CfUYP7z-o<~YR03Mx# z{Xq}>^uokMGBW^@Kd>t+X`3Nj2IvrUL!dPf%2E9MpsL#UTed_Xkdk!b#4}>ExK|X_ zdE&M%GzLE3&T0(SGH{klX{IHA+823V1C)fBu|qF!3#gi=7_bH2r-~O11PUIw<`! zu>WS^f+j6$v0SMKC>EyB%!xus=op%gL($ocA3hOL4m_V{h1rA;0S_TXKAtCjKg`tQ zyX@ds@WLR?7@B5x)_U&N23J(v(Yg?>3Jak8@Xt?E8|&*(EJYhG_<0gb(#Pn9CXG0Pqm$gedTq zWH_}#OaScpU%U|BlMTM?sT~bT(9fu~FgjfF#03C;-ZGD4_w;mS#&h~w6uHsUe|aLr zLw+w8XlTw(P6`wqb)!XkO;|bg;7M6sTdRBW88bl*Zcf-b%zp@&gZ%s^h1I-rb|Dz8 z-5NH7oA!{9(=-eorEp%i%9hUOYFu@kibD#Y*iBMxiG>q2$4tNm9Yi7}4 zeEk*Qv#3KlU|3uS=VAa8`t8w3M$Or|xj`_Rgizk!sN?V<&&9+>Ir;vnv(frPJ?V?5 z%vD~RSN`sW{7-Tsk`Z(JAQt<#oniKQJ?WW?5y)R z{}?*?&Rjc#jdEhzJh6Ue77?sMH@l*Bh4&Axd{I1`IsFxO#Scd-1y9Q_K4sf*RSx`6 zL7rQwy@d|h@e@-6Y6jkFre5gxtDO_oN>AU{$I~dS44Z=jBNM#AypM6jt8Me{0WFv@ zZ-eJCDDZ!GGkpBoEMdXqynh&&`1H`aX?q{5)n`9?$42PD_7=_BVtYLz;`XRIr9keZ z+2!45!-7ovS$nzm?H^CwnaUsNc(@#|&tLZx7KZf1@2?cEcIKKF*ynCLxy_?B~&aQ+%yz&reBB_JQNV9fjaQDL+QHW1*f(IsM}9}m~s%a z(>rNQj$KdW;=w;V_SFbdm~)Zs^xu8D@&3Q!&O98-_Wl2Qw4rQSv&@4i#5|NegvwU- zeJx}Z+4pTis3c`CdqT2|J;s(b`;zQ~$i9rMGYm7|Yp9-{=kxg;zrVi6*U@1v_uSq0 zbzl2=Ua$9g2Lwi)$D}nE>8@h2yAo1Tm&Ec~i$i+)jp#b}f?tAZFCd^UY}`!X)QN#+ zFu1iffXz{nH3yYfXZD+Et#7Kdf*sz@&OG8i1f<-x$w+0jS*FI^ZnP;}g*QSN>Yau# zKhu`KL&0F%G~Lyf{~WNvL`v|-??Ct|U;E{9vP;&ThTof~==lfnOV!~JAU0`7cFwog7)(%naiSrJG64)0A9Yc4S@Jvun7r_Cbjk|(W$02h0yTZ+pmh} zJ}mVtfJg^V=8EwW7N@A>Wo7YGOCT(QFLpuH`mJrLEhZTND35`u!aw-A*aIS5l;1T? z#5e`+k8DcVaa^R#S7rfmNRG_h0UjA(N6-JpKJ05<$_J>%4(i288Am{k6Y&Q@ExiMC zxc?vKaO}?lIxMWftdtcT>cyNQP*mJ&D|w~4VFtJC{d_maV%*E4AyZw`bKjsdqT z#}0P4(EYZ?Y*2tf6^?m*MmhC!9aTv6b(>l#G zA*8kk56l6tl!y)}d63@xs*xhx&v)l4vRIpI$(e)D&j43`e3ilb;{}2DLZuV2! zz^`DuI^K<6StSjn3}+I#zuc&-ZSLhZFxsvM$F?#;{E!l&lXiOOm_+`;i{|CUH-O*; zw5SiZ6Pg7Sz-$lFN~BFHjbTQ}K4sDUe6 zYi|`}X7kutK;IrkNJ(0tD>$ut7QfL7> zIq*2iie}Pvw^TQcfYw#cf|C?ioR?V)9SD3$P-h{p|Dv;4{l+AB zWJzH%%RYnHqVH5bYmSIlo_1R4g|jV;&O|%cB4DckEY>1#;mvQ3hx<)MLT6M{rfiO- zEhzy8;$HgMC`~|kA|;Q)CI0wJu;qT%OaYGdE^rpxYIsIFIh zami-SUb163Yt@(~yZtF%_^0u1LYT717 z`6EuZMwku}h(qZ_$#e;-720{bIUp!BoLN>vU}FUMxsIP^7o?&K{Nm7~u`?q$%wf*O z!zKQ9c(8Jt4DWpOw#kcrqn{|mjXM~R3DdTYuJ-os%EXjcQE~pf8+P}->xZMu-)_KY zyNn7QVW4No(=P%9LI@YY1xvw!oo+Mzb4@<+cZNDJqKE3_VUg zPAH9j5m&`;!hRUXqo=PgjGP#Jjws>K_554H0f_`*MNEBUYW%~iSfFNQH%=<`U+MJS z&Gp*3TYUDwz>Nx21o)VIt0K()ebk^Ny-}DJ{tV0O_^-!v!+~2PI`pTiVuPrvP+GtM zmmTQsfR_xVmM`NE(l8pxTgw0T0zng1IGNrV?O;Sk#tJ;VfPFbE2u-Z@Tz(=7)xM*D zsn`V!8|Sj#o=;XS^`0~v#;$$8$2W0T%8?1^4&Grgh6*=n<0*?IC;t4KkYKog%4?Z- zin96UBPkQ^{?W2oJ(lfZ!)>tYASpZXQ#wdIDe%Mi_S#m?MC#;5198=HjFOD~_<V|44E`Y%-j5Xye-5;~)fxw8;Tx`QLSvv^9K}`0rCfQ75Fu8imbcXei0#o@d<- z^LNl9#uA|}I(x7!kgj?GMW}{Bp(xNfSOAzItnA5jl}KJ3AQ(5|_C94ppEIil<6fjS zT-^SYUC+T^1jGf``qGuaU}35nu}C#+I^fS#0I%f#e=9*EDKO9vtEu(-8v>)b2IU)8 z&Mqz#z=@}%L@n(P05Yh^HHu z?ail0#0yDCQ~;aea@#M#$85SD!ith-Yn)YM-W7MN*hoR$Uz ztOs8ox1s~Y`)^Rh2=oUO0fZ8IS!!&%$`1g{57ZRtk2bfq#;Se9#Q2(?b=27Oz5(cO z4|nlTE~5XJl;$VbU*T8_?hV+IUSL+&3A&ap-vpbEz>cZb$pmq5!M=P#<<`omn1Dd| z;^8^c%i(VK?3oBd`YBxCXc_ zeC7u9IoN(v2ss5_phph&r%8pM0yO^d^XDKoYSy(kztIX#)$S`sR#xX8PyqW@>$PdE zzaOaN=nu8QFD!&rf$c`jWL%u02SF>G2!H(NL*_Zofo3M{{UjOLBw54E zw{Hq8viCKRySux>T&LekNr~8Ow8Bf9%(dKK1+)L95_bP4j~AF-g%6bphvFoK$K)6k zsw!|v3_54t-d63*%a4)2A^Or?h<{6dHY2wxv3)R~0fqF!p;z9}sxqK+H4O~PL11hU zoQ3>gS@?!7>l*CA-R7kT3)+7H0!To|9}og7Y0}ctNo_hnpDv>U=PB`e_sbiu{;Qp( z-emfl2G}^D0kA#%ffs@BO2bIU65s&;VbklB6TbHatbn(=o5K~Bsd;_w;hvk0dZ77W zlaz<{Q-M%~Njd3ZdYMR?9sHbns2nV{fAsK=A;u_jOs^&955>28K>u_m1A6__*5TJA zw%SHKbm+rBbZmee{2+Oo1h@G^G)YLyr-xmjb}MNs0e(PgS%l%x0dMdR#`NeqYRc<2yAAFLw?qkBWEW zT3iaVwCEx$m7)j(X6WJnxX26M_6Ko*)My&`idSP>f+M*YRsGeV67lYsP?zhKXYA=? zqWzx}|wj1m- z(G*&oPoIQzU3I@NP(3I+znp8pjv44PR=^#fsa&OJcWh=2Qj}XqG4?3T;BgLHOVuZ}iC0=-A<~FB@xr%f2QABmF z!15VUe2e?Lax#yCOC2W#zbx%-c-lv(-hB0!(|ZKMd<==+-zDrfE_k8|Mw1O*$9}t)b~z;c)>4J2TU%En z9zB{c5+!PIw?t<)$&;yU)C?+|}lQU{izQd1nS!E9WNo&|Aps z6doC%jO$L4laCkTd-y?@YjQpq{<>i2iLi!)bigugd* zkSdGOI)$a;wV& zQGs;Zbuv`wfhTOYb!;lC5ia5YLLvNi1HZAZXgL<)PyWf5T=z3lBGFp?_`Zrt1zBvr z@a_E!T`5Fzx##K-u@$P=qj(o2=cpkq^+wf>j5WpVOiY_&<0-DkW2u02^q`_Oj=j{I zRAb%YF72yoe3>A8La>%P!3*9}`6^wulbyk3;p1@SdFZ&5|JwM=&BzpJG>W%o;vO=F zkn_x>95}Z9iZ`UtfBDTW%Mvo!4Aqj=u)Z7mNez|nZ)l&+Ck5+~gI@A3P$|ptJn|ab z*&5FMg{B44nu2hVpcf{x_^?prFU)HlxkJZuyWD3=tJ9%d1>OOAPNk}&@ zF+ribv$+*=a&-WJXZKl#>wU`A56?Vna^km#@Shy_nvIBCU%iQ4!=F&jSmln*SHEDI zV1VRFb9Iwnc#r)YtAq=LcFIwP8%{2$lAPnSSG3r?_Cuj0cdCeqLP zHIDl<%GTCm>M`6jJ}M#FKHb%CB5`om4FS;q-T-Lh@cC3#Y8~N=F%V}8^1ghOa_NEG zfw&?xJ8I6n|6mBadb|tgw8fZ(rDkCG*JGkETw<@I}_mG}n zUY;QQ7`ok01WO$bj4?Dn$6_Xu|B`yBD2N&+Ieey*z|E#j;cEp8zDB<*aCA_Ab4g7%-DNkjc_n!5p>Gl}eTwfQy z%ePX0gyf?yaY9@^-e2xA?ho(X?em`C-g$CjzBnF`IlaN%dwIvA2vZ{-FEEI9c@xO6v4byervqDuwhdGnk*!<_|d(rde zU9hIC8FAVYPNTRg18&9V`;pEZm--7+>3fmWO%{_RB)54Fo~QX^OU3zaYjJksJyj?n z(n%l^if7NiPMT=$kYtUUJvh#nCb{Zs6!LAYXSejTAS(&U#XrEh!@pEx^5A}D(SpvvdHc7_?9?fXZC@u#X!D6g?C@3 zIweGc12@DD@;l^w(RX3Aota<%lP`7+mSuV7Zc3W_rz%Ow=IpaRIvH2FH zU8XE_fE!xIN--zmA+Oc8okZ}5H-r{-D-_gQ5wZ7AFIbJdj8Vph(ppZ8r~n2pJ?)XO*8U&}IY7$@^OP}QH9ELg1*H|mGN#%`uRp1O|ekjbZI_fmCYE4_oJ%VQlg7|oA?F>UT@T; zs8`{}PfTxj?P9jkvMrpB@#<5&8xF< z@kMTJvzn;UkuE`z~I)LC2 ztjZ0+m(s*J+T+A3$8op0mhN|Yxpg4-W!W;Z=hd3vyE_aVC%-MRRf_pz*Pu?yjUl%n z4<-r|D*ZNF=Uh3rsM5L>7%A~^9}H$xhxwBe>gq$5qnZ{*T=||cqT~mH^`wNy| zlk|A_6Alw<4j!K77t|JSG%e1x%_H;1pk=*zc9frv+bxL>0?=G#`{4L;OewXfQ! zul=~{o+`0@&qs2kCuz;hjInzoac=WUimBgcH62z$3YNwym$uE}rFD|DT zfY$&wXXL3nNUd*?`S`$_!#kzZh0DdMdm;w&jLlnC3>i-=3yS0>d-w{A5cGvIzqusA zw{-@;=o#g_eSc%Bc#(YpT_krrzm5jxlT|ifHMDTnwLP^gpMy^Mt$AK^O?k0Wr0QuL zN!`|JoWAa8L*Jzl@BB-HLyIP)=*_atu=y-eAHTm?2b-g`!(@Y%94HjE7amlwzf8H~ zNOFPfsno-wVV|$>T>jEInLW6B@45>nv(s2uFe%@DtQ0oUd@JFDbn$*;H^WwQ?zg=U zFw@KDD!vqWvzt3wqt%qJS-6VstHXWgXP)*peI~HvlQ&o&u*D3VHGUBEiLu4= zB@Uz43}YxMJZ1IT(s)~O8pgy*q7pbonQFJ~>pCqDFQC>!Z1t&AK_<3#YjWd^cK?{b zd%ADLW_`&q{IuiHT^t`et<|s?Q#N<&{A`@edX0G5@Y0D;>0YcIXDRiq4&|*c14%1M z)qVwQ7YUbr{u;#AS)(h_gqfCa3{IZ>aAER1ilf^po4

v6 zH|q7NPos(Ox5f+c&Ieb9Jj3D}BO5smRc<4*U(7!rpYVlS<6NW$9EQFxQ$UmV`eU6$ z2E_?KTkd$v&?98|MXkmJ# z5@-n-L57frm8KOc<7T=znP-JjsjV0K*d0=93MF3QI?%mq$dHY>ivzpOnT2aU4*up# zkfMuzR!Rvfd8^Bdg?gipK)cv71WnjHpn@b-iOz%M?#B9Ps3fYlU(q@H&K8Qjc2{CS z_bNz}QIcfHJDt!Fg|5gB%_IrEtsYC>QXQ+JRU&pd9NxiLCVs*V?s{E)Z07~&hRe=D zITd11WjlXHfF^0<5p`Lr?v7v+cW?8O7M3BXf8DN~<(`Y9 zi@Z)eFV~E-^e;(h>G)16=T#)cDkcz%8P>mt0g2{?4O@?a-h4Pk zT9jQ`0Yo+)5@FR07>pm%)uEC>Cv(j*B7`AD?ZK> Pk1JnSyOuA1JMe!1AufjJ literal 0 HcmV?d00001 diff --git a/Ghidra/Framework/Docking/src/main/java/docking/DefaultHelpService.java b/Ghidra/Framework/Docking/src/main/java/docking/DefaultHelpService.java deleted file mode 100644 index 34bd0ec52b..0000000000 --- a/Ghidra/Framework/Docking/src/main/java/docking/DefaultHelpService.java +++ /dev/null @@ -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(); - } -} diff --git a/Ghidra/Framework/Docking/src/main/java/docking/help/CustomTOCView.java b/Ghidra/Framework/Docking/src/main/java/docking/help/CustomTOCView.java deleted file mode 100644 index ff6b4e3ae9..0000000000 --- a/Ghidra/Framework/Docking/src/main/java/docking/help/CustomTOCView.java +++ /dev/null @@ -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; - } - } -} diff --git a/Ghidra/Framework/Docking/src/main/java/docking/help/DockingHelpBroker.java b/Ghidra/Framework/Docking/src/main/java/docking/help/DockingHelpBroker.java index 04d8054c5d..7049358cdf 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/help/DockingHelpBroker.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/help/DockingHelpBroker.java @@ -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); */ diff --git a/Ghidra/Framework/Docking/src/main/java/docking/help/GHelpBroker.java b/Ghidra/Framework/Docking/src/main/java/docking/help/GHelpBroker.java deleted file mode 100644 index 38d7deb8f6..0000000000 --- a/Ghidra/Framework/Docking/src/main/java/docking/help/GHelpBroker.java +++ /dev/null @@ -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 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. - *

- * 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); - } - - } -} diff --git a/Ghidra/Framework/Docking/src/main/java/docking/help/GHelpHTMLEditorKit.java b/Ghidra/Framework/Docking/src/main/java/docking/help/GHelpHTMLEditorKit.java deleted file mode 100644 index 6f8a41c1a4..0000000000 --- a/Ghidra/Framework/Docking/src/main/java/docking/help/GHelpHTMLEditorKit.java +++ /dev/null @@ -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. - *

- * 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 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 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 /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 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 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); - } - - } - -} diff --git a/Ghidra/Framework/Docking/src/main/java/docking/help/HelpService.java b/Ghidra/Framework/Docking/src/main/java/docking/help/HelpService.java deleted file mode 100644 index 699d12a995..0000000000 --- a/Ghidra/Framework/Docking/src/main/java/docking/help/HelpService.java +++ /dev/null @@ -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; - -/** - * HelpService 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. - * - *

Do not call this method will a null 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(); -} diff --git a/Ghidra/Framework/Docking/src/main/java/docking/theme/gui/ThemeColorTable.java b/Ghidra/Framework/Docking/src/main/java/docking/theme/gui/ThemeColorTable.java new file mode 100644 index 0000000000..fa27f8e86a --- /dev/null +++ b/Ghidra/Framework/Docking/src/main/java/docking/theme/gui/ThemeColorTable.java @@ -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 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(currentValue, themeValue); + } + return null; + } +} diff --git a/Ghidra/Framework/Docking/src/main/java/docking/theme/gui/ThemeColorTableModel.java b/Ghidra/Framework/Docking/src/main/java/docking/theme/gui/ThemeColorTableModel.java index 0b95e002e8..e65591431f 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/theme/gui/ThemeColorTableModel.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/theme/gui/ThemeColorTableModel.java @@ -66,6 +66,13 @@ public class ThemeColorTableModel extends GDynamicColumnTableModel"; } 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 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 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 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 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(); } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/theme/gui/ThemeFontTable.java b/Ghidra/Framework/Docking/src/main/java/docking/theme/gui/ThemeFontTable.java new file mode 100644 index 0000000000..6f9293e8f9 --- /dev/null +++ b/Ghidra/Framework/Docking/src/main/java/docking/theme/gui/ThemeFontTable.java @@ -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 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(currentValue, themeValue); + } + return null; + } +} diff --git a/Ghidra/Framework/Docking/src/main/java/docking/theme/gui/ThemeFontTableModel.java b/Ghidra/Framework/Docking/src/main/java/docking/theme/gui/ThemeFontTableModel.java index c01b8ebff7..05e229dc90 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/theme/gui/ThemeFontTableModel.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/theme/gui/ThemeFontTableModel.java @@ -100,7 +100,7 @@ public class ThemeFontTableModel extends GDynamicColumnTableModel"; } if (fontValue.getReferenceId() != null) { - return fontValue.getReferenceId(); + return "[" + fontValue.getReferenceId() + "]"; } Font font = fontValue.getRawValue(); @@ -178,8 +178,6 @@ public class ThemeFontTableModel extends GDynamicColumnTableModel { - 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 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(currentValue, themeValue); + } + return null; + } +} diff --git a/Ghidra/Framework/Docking/src/main/java/docking/theme/gui/ThemeIconTableModel.java b/Ghidra/Framework/Docking/src/main/java/docking/theme/gui/ThemeIconTableModel.java index 5deb69a4b6..e4888b8368 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/theme/gui/ThemeIconTableModel.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/theme/gui/ThemeIconTableModel.java @@ -229,8 +229,16 @@ public class ThemeIconTableModel extends GDynamicColumnTableModel the resource type (Color, Font, or Icon) + */ +public class ThemeTableContext extends ActionContext { + + private ThemeValue currentValue; + private ThemeValue themeValue; + + public ThemeTableContext(ThemeValue currentValue, ThemeValue 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 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 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); + } +} diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/conditiontestpanel/ConditionTestPanel.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/conditiontestpanel/ConditionTestPanel.java index 0f11262225..6942ae9c87 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/conditiontestpanel/ConditionTestPanel.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/conditiontestpanel/ConditionTestPanel.java @@ -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); diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/dialogs/MultiLineInputDialog.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/dialogs/MultiLineInputDialog.java index da402f4c5d..b7bfb1848e 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/dialogs/MultiLineInputDialog.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/dialogs/MultiLineInputDialog.java @@ -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); diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/textfield/HexOrDecimalInput.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/textfield/HexOrDecimalInput.java index 9614fa62ad..224fbf091d 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/textfield/HexOrDecimalInput.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/textfield/HexOrDecimalInput.java @@ -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); diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/textfield/IntegerTextField.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/textfield/IntegerTextField.java index 80cd18942a..6a22570d44 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/textfield/IntegerTextField.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/textfield/IntegerTextField.java @@ -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(); diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/textfield/LocalDateTextField.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/textfield/LocalDateTextField.java index 9b0d8a96f6..bdf9e96a16 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/textfield/LocalDateTextField.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/textfield/LocalDateTextField.java @@ -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); diff --git a/Ghidra/Framework/Docking/src/test/java/docking/theme/gui/ThemeUtilsTest.java b/Ghidra/Framework/Docking/src/test/java/docking/theme/gui/ThemeUtilsTest.java index b796e24a7a..79948be124 100644 --- a/Ghidra/Framework/Docking/src/test/java/docking/theme/gui/ThemeUtilsTest.java +++ b/Ghidra/Framework/Docking/src/test/java/docking/theme/gui/ThemeUtilsTest.java @@ -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() { diff --git a/Ghidra/Framework/Generic/src/main/java/generic/theme/ColorValue.java b/Ghidra/Framework/Generic/src/main/java/generic/theme/ColorValue.java index 764b740305..7b869230d2 100644 --- a/Ghidra/Framework/Generic/src/main/java/generic/theme/ColorValue.java +++ b/Ghidra/Framework/Generic/src/main/java/generic/theme/ColorValue.java @@ -149,4 +149,9 @@ public class ColorValue extends ThemeValue { return outputString; } + @Override + public void makeCurrentValue() { + Gui.setColor(this); + } + } diff --git a/Ghidra/Framework/Generic/src/main/java/generic/theme/FontValue.java b/Ghidra/Framework/Generic/src/main/java/generic/theme/FontValue.java index 51ce630368..89f514a580 100644 --- a/Ghidra/Framework/Generic/src/main/java/generic/theme/FontValue.java +++ b/Ghidra/Framework/Generic/src/main/java/generic/theme/FontValue.java @@ -223,4 +223,9 @@ public class FontValue extends ThemeValue { return "PLAIN"; } + @Override + public void makeCurrentValue() { + Gui.setFont(this); + } + } diff --git a/Ghidra/Framework/Generic/src/main/java/generic/theme/IconValue.java b/Ghidra/Framework/Generic/src/main/java/generic/theme/IconValue.java index ffdf7220a6..92fe807611 100644 --- a/Ghidra/Framework/Generic/src/main/java/generic/theme/IconValue.java +++ b/Ghidra/Framework/Generic/src/main/java/generic/theme/IconValue.java @@ -146,4 +146,9 @@ public class IconValue extends ThemeValue { return iconToString(value); } + @Override + public void makeCurrentValue() { + Gui.setIcon(this); + } + } diff --git a/Ghidra/Framework/Generic/src/main/java/generic/theme/ThemeValue.java b/Ghidra/Framework/Generic/src/main/java/generic/theme/ThemeValue.java index c1fa9c9ed8..85dd49b9b3 100644 --- a/Ghidra/Framework/Generic/src/main/java/generic/theme/ThemeValue.java +++ b/Ghidra/Framework/Generic/src/main/java/generic/theme/ThemeValue.java @@ -202,4 +202,9 @@ public abstract class ThemeValue implements Comparable> { return name + " (" + id + ", " + referenceId + ")"; } + /** + * Makes this value the current value for the application + */ + public abstract void makeCurrentValue(); + } diff --git a/Ghidra/Framework/Generic/src/main/java/generic/theme/laf/ComponentFontRegistry.java b/Ghidra/Framework/Generic/src/main/java/generic/theme/laf/ComponentFontRegistry.java index 4a58430d43..9ddc2dca6d 100644 --- a/Ghidra/Framework/Generic/src/main/java/generic/theme/laf/ComponentFontRegistry.java +++ b/Ghidra/Framework/Generic/src/main/java/generic/theme/laf/ComponentFontRegistry.java @@ -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) { - component.setFont(font); + Font existingFont = component.getFont(); + if (!Objects.equals(existingFont, font)) { + component.setFont(font); + } } } } diff --git a/Ghidra/Framework/Generic/src/main/java/generic/theme/laf/LookAndFeelManager.java b/Ghidra/Framework/Generic/src/main/java/generic/theme/laf/LookAndFeelManager.java index 12ece6a4e4..6b2afa1b26 100644 --- a/Ghidra/Framework/Generic/src/main/java/generic/theme/laf/LookAndFeelManager.java +++ b/Ghidra/Framework/Generic/src/main/java/generic/theme/laf/LookAndFeelManager.java @@ -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 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); diff --git a/Ghidra/Framework/Generic/src/main/java/generic/theme/laf/NimbusLookAndFeelManager.java b/Ghidra/Framework/Generic/src/main/java/generic/theme/laf/NimbusLookAndFeelManager.java index 7f17b9bdeb..da432acb63 100644 --- a/Ghidra/Framework/Generic/src/main/java/generic/theme/laf/NimbusLookAndFeelManager.java +++ b/Ghidra/Framework/Generic/src/main/java/generic/theme/laf/NimbusLookAndFeelManager.java @@ -48,8 +48,8 @@ public class NimbusLookAndFeelManager extends LookAndFeelManager { public void fontsChanged(Set affectedJavaIds) { if (!affectedJavaIds.isEmpty()) { reinstallNimubus(); - updateComponentFonts(affectedJavaIds); } + updateAllRegisteredComponentFonts(); repaintAll(); } diff --git a/Ghidra/Framework/Generic/src/main/java/resources/IconProvider.java b/Ghidra/Framework/Generic/src/main/java/resources/IconProvider.java index e47046c6b9..1ea123f154 100644 --- a/Ghidra/Framework/Generic/src/main/java/resources/IconProvider.java +++ b/Ghidra/Framework/Generic/src/main/java/resources/IconProvider.java @@ -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(); } diff --git a/Ghidra/Framework/Docking/src/main/java/resources/Icons.java b/Ghidra/Framework/Generic/src/main/java/resources/Icons.java similarity index 95% rename from Ghidra/Framework/Docking/src/main/java/resources/Icons.java rename to Ghidra/Framework/Generic/src/main/java/resources/Icons.java index 478311eb67..87aac38ca0 100644 --- a/Ghidra/Framework/Docking/src/main/java/resources/Icons.java +++ b/Ghidra/Framework/Generic/src/main/java/resources/Icons.java @@ -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; + 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"); } - - URL url = icon.getUrl(); - if (url != null) { - return url; - } - Msg.debug(Icons.class, "Unable to get URL for icon"); return null; } diff --git a/Ghidra/Framework/Generic/src/test/java/generic/theme/GuiTest.java b/Ghidra/Framework/Generic/src/test/java/generic/theme/GuiTest.java index 535860d49b..5a7ec20ae4 100644 --- a/Ghidra/Framework/Generic/src/test/java/generic/theme/GuiTest.java +++ b/Ghidra/Framework/Generic/src/test/java/generic/theme/GuiTest.java @@ -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); diff --git a/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/GraphComponent.java b/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/GraphComponent.java index 199541898d..169c2e6656 100644 --- a/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/GraphComponent.java +++ b/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/GraphComponent.java @@ -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; diff --git a/Ghidra/Framework/Graph/src/test/java/ghidra/graph/algo/viewer/AlgorithmTestSteppingVertex.java b/Ghidra/Framework/Graph/src/test/java/ghidra/graph/algo/viewer/AlgorithmTestSteppingVertex.java index 557e7d2b53..407a2933ec 100644 --- a/Ghidra/Framework/Graph/src/test/java/ghidra/graph/algo/viewer/AlgorithmTestSteppingVertex.java +++ b/Ghidra/Framework/Graph/src/test/java/ghidra/graph/algo/viewer/AlgorithmTestSteppingVertex.java @@ -88,7 +88,7 @@ public class AlgorithmTestSteppingVertex 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"); diff --git a/Ghidra/Framework/Graph/src/test/java/ghidra/service/graph/GraphDisplayOptionsTest.java b/Ghidra/Framework/Graph/src/test/java/ghidra/service/graph/GraphDisplayOptionsTest.java index 17f33d0678..193033bc38 100644 --- a/Ghidra/Framework/Graph/src/test/java/ghidra/service/graph/GraphDisplayOptionsTest.java +++ b/Ghidra/Framework/Graph/src/test/java/ghidra/service/graph/GraphDisplayOptionsTest.java @@ -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"); diff --git a/Ghidra/Framework/Help/src/main/java/docking/DefaultHelpService.java b/Ghidra/Framework/Help/src/main/java/docking/DefaultHelpService.java index 42ff57ade0..b103ee06d0 100644 --- a/Ghidra/Framework/Help/src/main/java/docking/DefaultHelpService.java +++ b/Ghidra/Framework/Help/src/main/java/docking/DefaultHelpService.java @@ -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); diff --git a/Ghidra/Framework/Help/src/main/java/help/GHelpBroker.java b/Ghidra/Framework/Help/src/main/java/help/GHelpBroker.java index 2912226ba5..54484b9c1d 100644 --- a/Ghidra/Framework/Help/src/main/java/help/GHelpBroker.java +++ b/Ghidra/Framework/Help/src/main/java/help/GHelpBroker.java @@ -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) { diff --git a/Ghidra/Framework/Help/src/main/java/help/GHelpHTMLEditorKit.java b/Ghidra/Framework/Help/src/main/java/help/GHelpHTMLEditorKit.java index 9598c6100a..59b58ebec9 100644 --- a/Ghidra/Framework/Help/src/main/java/help/GHelpHTMLEditorKit.java +++ b/Ghidra/Framework/Help/src/main/java/help/GHelpHTMLEditorKit.java @@ -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; diff --git a/Ghidra/Framework/Help/src/main/java/help/HelpService.java b/Ghidra/Framework/Help/src/main/java/help/HelpService.java index bdf2e643be..969c97730a 100644 --- a/Ghidra/Framework/Help/src/main/java/help/HelpService.java +++ b/Ghidra/Framework/Help/src/main/java/help/HelpService.java @@ -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(); } diff --git a/Ghidra/Framework/Project/build.gradle b/Ghidra/Framework/Project/build.gradle index 0805a5b938..d13ff51ca6 100644 --- a/Ghidra/Framework/Project/build.gradle +++ b/Ghidra/Framework/Project/build.gradle @@ -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' diff --git a/Ghidra/Framework/Project/certification.manifest b/Ghidra/Framework/Project/certification.manifest index 642d3e7b64..87a4a528ff 100644 --- a/Ghidra/Framework/Project/certification.manifest +++ b/Ghidra/Framework/Project/certification.manifest @@ -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| diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/app/plugin/gui/ThemeManagerPlugin.java b/Ghidra/Framework/Project/src/main/java/ghidra/app/plugin/gui/ThemeManagerPlugin.java index c71d9ed133..764974e0e3 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/app/plugin/gui/ThemeManagerPlugin.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/app/plugin/gui/ThemeManagerPlugin.java @@ -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); diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/ImportGhidraToolsDialog.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/ImportGhidraToolsDialog.java index 58b05bc612..235f27074f 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/ImportGhidraToolsDialog.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/ImportGhidraToolsDialog.java @@ -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++; } diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/SetToolAssociationsDialog.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/SetToolAssociationsDialog.java index f706036933..0ffa03142a 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/SetToolAssociationsDialog.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/SetToolAssociationsDialog.java @@ -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,8 +240,10 @@ class SetToolAssociationsDialog extends DialogComponentProvider { private class ContentHandlerComparator implements Comparator { @Override public int compare(ToolAssociationInfo o1, ToolAssociationInfo o2) { - return o1.getContentHandler().getContentType().compareTo( - o2.getContentHandler().getContentType()); + 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())) { diff --git a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/AbstractSearchScreenShots.java b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/AbstractSearchScreenShots.java index 3e0d03cbdc..eec2c8d5c8 100644 --- a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/AbstractSearchScreenShots.java +++ b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/AbstractSearchScreenShots.java @@ -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() { diff --git a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/AutoAnalysisPluginScreenShots.java b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/AutoAnalysisPluginScreenShots.java index e7512c51e6..0817d08c90 100644 --- a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/AutoAnalysisPluginScreenShots.java +++ b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/AutoAnalysisPluginScreenShots.java @@ -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 - public void testAutoAnalysis() { - Color darkGreen = new Color(20, 154, 65); - Color darkBlue = new Color(10, 62, 149); + @Test + public void testAutoAnalysis() { + 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,14 +73,14 @@ public class AutoAnalysisPluginScreenShots extends GhidraScreenShotGenerator { drawArrow(darkBlue, p3, new Point(404, 88)); } -@Test - public void testCaptureAutoAnalysisOptions() { + @Test + public void testCaptureAutoAnalysisOptions() { showAnalysisOptions("Data Reference"); captureDialog(800, 400); } -@Test - public void testCaptureBackgroundAnalysisTasks() throws InterruptedException { + @Test + public void testCaptureBackgroundAnalysisTasks() throws InterruptedException { CountDownLatch start = new CountDownLatch(1); CountDownLatch end = new CountDownLatch(1); TestBackgroundCommand cmd = new TestBackgroundCommand(start, end); @@ -92,8 +94,8 @@ public class AutoAnalysisPluginScreenShots extends GhidraScreenShotGenerator { crop(new Rectangle(width - 400, height - 120, 400, 120)); } -@Test - public void testCaptureProgramOptions() { + @Test + public void testCaptureProgramOptions() { showProgramOptions("Analyzers"); DialogComponentProvider dialog = getDialog(); Component comp = findComponentByName(dialog.getComponent(), "Analysis Panel"); @@ -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 { diff --git a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/BlockModelScreenShots.java b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/BlockModelScreenShots.java index a2be1190e3..d111e8c4b1 100644 --- a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/BlockModelScreenShots.java +++ b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/BlockModelScreenShots.java @@ -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,8 +177,9 @@ public class BlockModelScreenShots extends GhidraScreenShotGenerator { if (fieldFactory.getFieldName().indexOf("XRef") != -1) { formatModel.removeFactory(row, col); } - else if (fieldFactory.getFieldName().equals( - EolCommentFieldFactory.FIELD_NAME)) { + else if (fieldFactory.getFieldName() + .equals( + EolCommentFieldFactory.FIELD_NAME)) { formatModel.removeFactory(row, col); } else if (fieldFactory.getFieldName().equals(AddressFieldFactory.FIELD_NAME)) { diff --git a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/ClipboardPluginScreenShots.java b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/ClipboardPluginScreenShots.java index 12cf132b93..676a77304a 100644 --- a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/ClipboardPluginScreenShots.java +++ b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/ClipboardPluginScreenShots.java @@ -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,21 +150,18 @@ public class ClipboardPluginScreenShots extends GhidraScreenShotGenerator { Object listPanel = getInstanceField("listPanel", copySpecialDialog); final JList list = (JList) getInstanceField("list", listPanel); - runSwing(new Runnable() { - @Override - public void run() { - ListModel model = list.getModel(); - int size = model.getSize(); - for (int i = 0; i < size; i++) { - Object value = model.getElementAt(i); - if ("Labels and Comments".equals(value.toString())) { - list.setSelectedIndex(i); - return; - } + runSwing(() -> { + ListModel model = list.getModel(); + int size = model.getSize(); + for (int i = 0; i < size; i++) { + Object value = model.getElementAt(i); + if ("Labels and Comments".equals(value.toString())) { + list.setSelectedIndex(i); + return; } - - throw new RuntimeException("Could not find 'Labels and Comments' copy action"); } + + throw new RuntimeException("Could not find 'Labels and Comments' copy action"); }); waitForSwing(); } diff --git a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/CodeBrowserPluginScreenShots.java b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/CodeBrowserPluginScreenShots.java index 758592eec5..6bac7e2829 100644 --- a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/CodeBrowserPluginScreenShots.java +++ b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/CodeBrowserPluginScreenShots.java @@ -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); diff --git a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/DecompilePluginScreenShots.java b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/DecompilePluginScreenShots.java index 86e3c8a3b2..b068e7fb28 100644 --- a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/DecompilePluginScreenShots.java +++ b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/DecompilePluginScreenShots.java @@ -24,15 +24,16 @@ import org.junit.Test; import docking.ComponentProvider; import docking.DockableComponent; +import generic.theme.GThemeDefaults.Colors.Palette; import ghidra.app.plugin.core.codebrowser.CodeViewerProvider; import ghidra.app.plugin.core.datamgr.DataTypesProvider; import ghidra.app.plugin.core.programtree.ViewManagerComponentProvider; public class DecompilePluginScreenShots extends GhidraScreenShotGenerator { - private static final Color DARK_BLUE = new Color(0, 0, 128); - private static final Color DARK_GREEN = new Color(0, 128, 0); - private static final Color YELLOW_ORANGE = new Color(155, 150, 50); - private static final Color PURPLE = new Color(155, 50, 155); + private static final Color DARK_BLUE = Palette.getColor("navy"); + private static final Color DARK_GREEN = Palette.GREEN; + private static final Color YELLOW_ORANGE = Palette.getColor("darkkhaki"); + private static final Color PURPLE = Palette.PURPLE; public DecompilePluginScreenShots() { super(); @@ -57,12 +58,12 @@ public class DecompilePluginScreenShots extends GhidraScreenShotGenerator { @Test public void testDefuse() { TextFormatter tf = new TextFormatter(17, 400, 4, 5, 0); - TextFormatterContext hl = new TextFormatterContext(Color.BLACK, Color.YELLOW); - TextFormatterContext red = new TextFormatterContext(Color.RED, Color.WHITE); - TextFormatterContext blue = new TextFormatterContext(Color.BLUE, Color.WHITE); - TextFormatterContext green = new TextFormatterContext(Color.GREEN, Color.WHITE); + TextFormatterContext hl = new TextFormatterContext(Palette.BLACK, Palette.YELLOW); + TextFormatterContext red = new TextFormatterContext(Palette.RED, Palette.WHITE); + TextFormatterContext blue = new TextFormatterContext(Palette.BLUE, Palette.WHITE); + TextFormatterContext green = new TextFormatterContext(Palette.GREEN, Palette.WHITE); TextFormatterContext cursorhl = - new TextFormatterContext(Color.BLACK, Color.YELLOW, Color.RED); + new TextFormatterContext(Palette.BLACK, Palette.YELLOW, Palette.RED); tf.writeln("|void| |max_retry|(node *ptr)", blue, red); tf.writeln(""); @@ -87,12 +88,12 @@ public class DecompilePluginScreenShots extends GhidraScreenShotGenerator { @Test public void testForwardSlice() { TextFormatter tf = new TextFormatter(16, 500, 4, 5, 0); - TextFormatterContext hl = new TextFormatterContext(Color.BLACK, Color.YELLOW); - TextFormatterContext red = new TextFormatterContext(Color.RED); - TextFormatterContext blue = new TextFormatterContext(Color.BLUE); - TextFormatterContext green = new TextFormatterContext(Color.GREEN); + TextFormatterContext hl = new TextFormatterContext(Palette.BLACK, Palette.YELLOW); + TextFormatterContext red = new TextFormatterContext(Palette.RED); + TextFormatterContext blue = new TextFormatterContext(Palette.BLUE); + TextFormatterContext green = new TextFormatterContext(Palette.GREEN); TextFormatterContext cursorhl = - new TextFormatterContext(Color.BLACK, Color.YELLOW, Color.RED); + new TextFormatterContext(Palette.BLACK, Palette.YELLOW, Palette.RED); tf.writeln(" a = psParm2->id;"); tf.writeln(" |b| = |max_alpha|(psParm1->next,psParm1->id);", cursorhl, red); @@ -117,13 +118,13 @@ public class DecompilePluginScreenShots extends GhidraScreenShotGenerator { @Test public void testBackwardSlice() { TextFormatter tf = new TextFormatter(16, 500, 4, 5, 0); - TextFormatterContext hl = new TextFormatterContext(Color.BLACK, Color.YELLOW); - TextFormatterContext red = new TextFormatterContext(Color.RED, Color.WHITE); - TextFormatterContext blue = new TextFormatterContext(Color.BLUE, Color.WHITE); - TextFormatterContext green = new TextFormatterContext(Color.GREEN, Color.WHITE); - TextFormatterContext greenhl = new TextFormatterContext(Color.GREEN, Color.YELLOW); + TextFormatterContext hl = new TextFormatterContext(Palette.BLACK, Palette.YELLOW); + TextFormatterContext red = new TextFormatterContext(Palette.RED, Palette.WHITE); + TextFormatterContext blue = new TextFormatterContext(Palette.BLUE, Palette.WHITE); + TextFormatterContext green = new TextFormatterContext(Palette.GREEN, Palette.WHITE); + TextFormatterContext greenhl = new TextFormatterContext(Palette.GREEN, Palette.YELLOW); TextFormatterContext cursorhl = - new TextFormatterContext(Color.BLACK, Color.YELLOW, Color.RED); + new TextFormatterContext(Palette.BLACK, Palette.YELLOW, Palette.RED); tf.writeln(" |a| = |psParm2|->id;", hl, hl); tf.writeln(" b = |max_alpha|(|psParm1|->next,|psParm1|->id);", red, hl, hl); @@ -188,12 +189,12 @@ public class DecompilePluginScreenShots extends GhidraScreenShotGenerator { Font font = new Font("Monospaced", Font.PLAIN, 12); TextFormatter tf = new TextFormatter(font, 15, 400, 4, 14, 1); - TextFormatterContext blue = new TextFormatterContext(Color.BLUE); + TextFormatterContext blue = new TextFormatterContext(Palette.BLUE); TextFormatterContext darkBlue = new TextFormatterContext(DARK_BLUE); TextFormatterContext darkGreen = new TextFormatterContext(DARK_GREEN); TextFormatterContext orange = new TextFormatterContext(YELLOW_ORANGE); TextFormatterContext purple = new TextFormatterContext(PURPLE); - tf.colorLines(new Color(180, 255, 180), 9, 1); + tf.colorLines(Palette.getColor("palegreen"), 9, 1); // @formatter:off tf.writeln("|8b 40 0c| |MOV| |EAX|,|Oxc|[|EAX|]", blue, darkBlue, orange, darkGreen, orange ); @@ -220,11 +221,12 @@ public class DecompilePluginScreenShots extends GhidraScreenShotGenerator { Font font = new Font("Monospaced", Font.PLAIN, 13); TextFormatter tf = new TextFormatter(font, 15, 400, 4, 2, 0); - TextFormatterContext blue = new TextFormatterContext(Color.BLUE); - TextFormatterContext green = new TextFormatterContext(Color.GREEN); - TextFormatterContext hl = new TextFormatterContext(Color.BLACK, Color.YELLOW); - TextFormatterContext greenhl = new TextFormatterContext(Color.GREEN, Color.YELLOW); - TextFormatterContext cursor = new TextFormatterContext(Color.WHITE, Color.WHITE, Color.RED); + TextFormatterContext blue = new TextFormatterContext(Palette.BLUE); + TextFormatterContext green = new TextFormatterContext(Palette.GREEN); + TextFormatterContext hl = new TextFormatterContext(Palette.BLACK, Palette.YELLOW); + TextFormatterContext greenhl = new TextFormatterContext(Palette.GREEN, Palette.YELLOW); + TextFormatterContext cursor = + new TextFormatterContext(Palette.WHITE, Palette.WHITE, Palette.RED); // @formatter:off tf.writeln(" dword dVar1:"); @@ -252,10 +254,11 @@ public class DecompilePluginScreenShots extends GhidraScreenShotGenerator { Font font = new Font("Monospaced", Font.PLAIN, 13); TextFormatter tf = new TextFormatter(font, 15, 400, 4, 2, 0); - TextFormatterContext blue = new TextFormatterContext(Color.BLUE); - TextFormatterContext green = new TextFormatterContext(Color.GREEN); - TextFormatterContext hl = new TextFormatterContext(Color.BLACK, Color.YELLOW); - TextFormatterContext cursor = new TextFormatterContext(Color.WHITE, Color.WHITE, Color.RED); + TextFormatterContext blue = new TextFormatterContext(Palette.BLUE); + TextFormatterContext green = new TextFormatterContext(Palette.GREEN); + TextFormatterContext hl = new TextFormatterContext(Palette.BLACK, Palette.YELLOW); + TextFormatterContext cursor = + new TextFormatterContext(Palette.WHITE, Palette.WHITE, Palette.RED); // @formatter:off tf.writeln(" dword dVar1:"); diff --git a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/DiffScreenShots.java b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/DiffScreenShots.java index f7d35e946c..bbcbab9d2e 100644 --- a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/DiffScreenShots.java +++ b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/DiffScreenShots.java @@ -15,7 +15,6 @@ */ package help.screenshot; -import java.awt.Color; import java.awt.Font; import javax.swing.*; @@ -24,6 +23,8 @@ import org.junit.Test; import docking.ComponentProvider; import docking.DialogComponentProvider; +import generic.theme.GThemeDefaults.Colors; +import generic.theme.GThemeDefaults.Colors.Palette; import ghidra.app.nav.ListingPanelContainer; import ghidra.app.plugin.core.codebrowser.CodeViewerProvider; import ghidra.app.services.ProgramManager; @@ -135,8 +136,8 @@ public class DiffScreenShots extends GhidraScreenShotGenerator { JMenu jMenu = new JMenu(); Font font = jMenu.getFont().deriveFont(11f); TextFormatter tf = new TextFormatter(font, 3, 220, 0, 20, 3); - TextFormatterContext white = new TextFormatterContext(Color.WHITE); - tf.colorLines(new Color(60, 115, 200), 2, 1); + TextFormatterContext white = new TextFormatterContext(Colors.BACKGROUND); + tf.colorLines(Palette.getColor("cornflowerblue"), 2, 1); tf.writeln("Set Ignore for All Apply Settings"); tf.writeln("Set Replace for All Apply Settings"); diff --git a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/DisassembledViewPluginScreenShots.java b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/DisassembledViewPluginScreenShots.java index 12b044b152..33724166b0 100644 --- a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/DisassembledViewPluginScreenShots.java +++ b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/DisassembledViewPluginScreenShots.java @@ -22,6 +22,7 @@ import javax.swing.SwingUtilities; import org.junit.Test; import docking.ComponentProvider; +import generic.theme.GThemeDefaults.Colors.Palette; import ghidra.app.plugin.core.datamgr.DataTypesProvider; import ghidra.app.plugin.core.programtree.ViewManagerComponentProvider; @@ -60,7 +61,7 @@ public class DisassembledViewPluginScreenShots extends GhidraScreenShotGenerator size.height -= (2 * offset);// up r.setSize(size); - drawRectangle(Color.YELLOW, r, 10); + drawRectangle(Palette.YELLOW, r, 10); } } diff --git a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/FrontEndPluginScreenShots.java b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/FrontEndPluginScreenShots.java index 34622e93ec..d26eb3a9b2 100644 --- a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/FrontEndPluginScreenShots.java +++ b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/FrontEndPluginScreenShots.java @@ -33,6 +33,7 @@ import docking.widgets.filechooser.GhidraFileChooser; import docking.widgets.table.GTable; import docking.wizard.WizardManager; import docking.wizard.WizardPanel; +import generic.theme.GThemeDefaults.Colors; import ghidra.app.plugin.core.archive.RestoreDialog; import ghidra.framework.data.GhidraFileData; import ghidra.framework.main.*; @@ -712,7 +713,7 @@ public class FrontEndPluginScreenShots extends GhidraScreenShotGenerator { private void captureIconAndText(Icon labelImage, String text) { final JLabel label = new JLabel(text); - label.setBackground(Color.WHITE); + label.setBackground(Colors.BACKGROUND); label.setOpaque(true); label.setIcon(labelImage); label.setHorizontalAlignment(SwingConstants.CENTER); diff --git a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/FunctionGraphPluginScreenShots.java b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/FunctionGraphPluginScreenShots.java index 811e2cd876..5d8ef59385 100644 --- a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/FunctionGraphPluginScreenShots.java +++ b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/FunctionGraphPluginScreenShots.java @@ -41,6 +41,7 @@ import edu.uci.ics.jung.graph.Graph; import edu.uci.ics.jung.visualization.VisualizationServer; import edu.uci.ics.jung.visualization.VisualizationViewer; import generic.test.TestUtils; +import generic.theme.GThemeDefaults.Colors.Palette; import ghidra.app.cmd.function.DeleteFunctionCmd; import ghidra.app.cmd.label.AddLabelCmd; import ghidra.app.plugin.core.functiongraph.AbstractFunctionGraphTest; @@ -318,7 +319,7 @@ public class FunctionGraphPluginScreenShots extends AbstractFunctionGraphTest { int h = (graphImage1.getHeight(null) - 10) - y1; - g.setColor(Color.BLACK); + g.setColor(Palette.BLACK); g.fillRect(x1, y1, w, h); screen.image = fullImage; @@ -530,13 +531,13 @@ public class FunctionGraphPluginScreenShots extends AbstractFunctionGraphTest { Rectangle rect = new Rectangle(x, y, w, h); // drop shadow - Color color = Color.GRAY; + Color color = Palette.GRAY; screen.drawRectangle(color, rect, boxThickness); // box x -= 1; y -= 2; - color = new Color(0xB5, 0xDE, 0x2F); + color = Palette.getColor("greenyellow"); rect.x = x; rect.y = y; screen.drawRectangle(color, rect, boxThickness); diff --git a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/GlossaryScreenShots.java b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/GlossaryScreenShots.java index 4140205f7d..7c6ed8a43f 100644 --- a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/GlossaryScreenShots.java +++ b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/GlossaryScreenShots.java @@ -15,24 +15,28 @@ */ package help.screenshot; -import java.awt.*; +import java.awt.Graphics; +import java.awt.Point; import java.awt.image.BufferedImage; import org.junit.Test; +import generic.theme.GThemeDefaults.Colors; +import generic.theme.GThemeDefaults.Colors.Palette; + public class GlossaryScreenShots extends GhidraScreenShotGenerator { public GlossaryScreenShots() { super(); } -@Test - public void testBigEndian() { + @Test + public void testBigEndian() { //Draw empty white rectangle image = new BufferedImage(450, 100, BufferedImage.TYPE_INT_ARGB); Graphics g = image.getGraphics(); - g.setColor(Color.WHITE); + g.setColor(Colors.BACKGROUND); g.fillRect(0, 0, 450, 125); //Draw box with line in the middle @@ -44,16 +48,16 @@ public class GlossaryScreenShots extends GhidraScreenShotGenerator { Point p5 = new Point(225, 10); Point p6 = new Point(225, 50); - drawLine(Color.BLACK, 1, p1, p2); - drawLine(Color.BLACK, 1, p2, p3); - drawLine(Color.BLACK, 1, p3, p4); - drawLine(Color.BLACK, 1, p4, p1); + drawLine(Palette.BLACK, 1, p1, p2); + drawLine(Palette.BLACK, 1, p2, p3); + drawLine(Palette.BLACK, 1, p3, p4); + drawLine(Palette.BLACK, 1, p4, p1); - drawLine(Color.BLACK, 1, p5, p6); + drawLine(Palette.BLACK, 1, p5, p6); //Draw Text in boxes - drawText("high-order byte", Color.BLACK, new Point(80, 35), 12); - drawText("low-order byte", Color.BLACK, new Point(285, 35), 12); + drawText("high-order byte", Colors.FOREGROUND, new Point(80, 35), 12); + drawText("low-order byte", Colors.FOREGROUND, new Point(285, 35), 12); //Draw arrows Point p7 = new Point(30, 50); @@ -61,22 +65,22 @@ public class GlossaryScreenShots extends GhidraScreenShotGenerator { Point p9 = new Point(225, 50); Point p10 = new Point(225, 80); - drawArrow(Color.BLACK, 1, p8, p7, 6); - drawArrow(Color.BLACK, 1, p10, p9, 6); + drawArrow(Palette.BLACK, 1, p8, p7, 6); + drawArrow(Palette.BLACK, 1, p10, p9, 6); //Draw arrow text - drawText("addr A", Color.BLACK, new Point(15, 93), 12); - drawText("addr A+1", Color.BLACK, new Point(200, 93), 12); + drawText("addr A", Colors.FOREGROUND, new Point(15, 93), 12); + drawText("addr A+1", Colors.FOREGROUND, new Point(200, 93), 12); } -@Test - public void testLittleEndian() { + @Test + public void testLittleEndian() { //Draw empty white rectangle image = new BufferedImage(450, 100, BufferedImage.TYPE_INT_ARGB); Graphics g = image.getGraphics(); - g.setColor(Color.WHITE); + g.setColor(Palette.WHITE); g.fillRect(0, 0, 450, 125); //Draw box with line in the middle @@ -88,16 +92,16 @@ public class GlossaryScreenShots extends GhidraScreenShotGenerator { Point p5 = new Point(225, 10); Point p6 = new Point(225, 50); - drawLine(Color.BLACK, 1, p1, p2); - drawLine(Color.BLACK, 1, p2, p3); - drawLine(Color.BLACK, 1, p3, p4); - drawLine(Color.BLACK, 1, p4, p1); + drawLine(Palette.BLACK, 1, p1, p2); + drawLine(Palette.BLACK, 1, p2, p3); + drawLine(Palette.BLACK, 1, p3, p4); + drawLine(Palette.BLACK, 1, p4, p1); - drawLine(Color.BLACK, 1, p5, p6); + drawLine(Palette.BLACK, 1, p5, p6); //Draw Text in boxes - drawText("high-order byte", Color.BLACK, new Point(80, 35), 12); - drawText("low-order byte", Color.BLACK, new Point(285, 35), 12); + drawText("high-order byte", Colors.FOREGROUND, new Point(80, 35), 12); + drawText("low-order byte", Colors.FOREGROUND, new Point(285, 35), 12); //Draw arrows Point p7 = new Point(430, 50); @@ -105,12 +109,12 @@ public class GlossaryScreenShots extends GhidraScreenShotGenerator { Point p9 = new Point(225, 50); Point p10 = new Point(225, 80); - drawArrow(Color.BLACK, 1, p8, p7, 6); - drawArrow(Color.BLACK, 1, p10, p9, 6); + drawArrow(Palette.BLACK, 1, p8, p7, 6); + drawArrow(Palette.BLACK, 1, p10, p9, 6); //Draw arrow text - drawText("addr A+1", Color.BLACK, new Point(200, 93), 12); - drawText("addr A", Color.BLACK, new Point(413, 93), 12); + drawText("addr A+1", Colors.FOREGROUND, new Point(200, 93), 12); + drawText("addr A", Colors.FOREGROUND, new Point(413, 93), 12); } } diff --git a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/MemoryMapPluginScreenShots.java b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/MemoryMapPluginScreenShots.java index 646804c486..f6f89d1991 100644 --- a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/MemoryMapPluginScreenShots.java +++ b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/MemoryMapPluginScreenShots.java @@ -15,7 +15,8 @@ */ package help.screenshot; -import java.awt.*; +import java.awt.Graphics; +import java.awt.Point; import java.awt.image.BufferedImage; import javax.swing.JComponent; @@ -27,6 +28,8 @@ import docking.ComponentProvider; import docking.DialogComponentProvider; import docking.action.DockingAction; import docking.widgets.combobox.GhidraComboBox; +import generic.theme.GThemeDefaults.Colors; +import generic.theme.GThemeDefaults.Colors.Palette; import ghidra.util.exception.AssertException; import ghidra.util.table.GhidraTable; @@ -36,8 +39,8 @@ public class MemoryMapPluginScreenShots extends GhidraScreenShotGenerator { super(); } -@Test - public void testMemoryMap() { + @Test + public void testMemoryMap() { performAction("Memory Map", "DockingWindows", true); @@ -48,52 +51,52 @@ public class MemoryMapPluginScreenShots extends GhidraScreenShotGenerator { captureIsolatedComponent(component, 800, 225); } -@Test - public void testAddMemoryBlock() { + @Test + public void testAddMemoryBlock() { performAction("Add Block", "MemoryMapPlugin", false); captureDialog(); } -@Test - public void testBitOverlayAddresses() { + @Test + public void testBitOverlayAddresses() { //Draw empty white rectangle image = new BufferedImage(450, 175, BufferedImage.TYPE_INT_ARGB); Graphics g = image.getGraphics(); - g.setColor(Color.WHITE); + g.setColor(Colors.BACKGROUND); g.fillRect(0, 0, 450, 175); //Draw Title and subtitle - drawText("Bit Overlay Addresses", Color.BLACK, new Point(160, 30), 18); - drawText("Byte Memory", Color.BLACK, new Point(15, 30), 10); - drawText("Addresses", Color.BLACK, new Point(20, 40), 10); + drawText("Bit Overlay Addresses", Colors.FOREGROUND, new Point(160, 30), 18); + drawText("Byte Memory", Colors.FOREGROUND, new Point(15, 30), 10); + drawText("Addresses", Colors.FOREGROUND, new Point(20, 40), 10); //Draw text inside and next to boxes - drawText("00008100", Color.BLACK, new Point(15, 80), 12); - drawText("00008100", Color.BLACK, new Point(15, 130), 12); + drawText("00008100", Colors.FOREGROUND, new Point(15, 80), 12); + drawText("00008100", Colors.FOREGROUND, new Point(15, 130), 12); - drawText("MSB", Color.BLACK, new Point(90, 60), 10); - drawText("LSB", Color.BLACK, new Point(370, 60), 10); + drawText("MSB", Colors.FOREGROUND, new Point(90, 60), 10); + drawText("LSB", Colors.FOREGROUND, new Point(370, 60), 10); - drawText("0007", Color.BLACK, new Point(90, 80), 10); - drawText("0006", Color.BLACK, new Point(130, 80), 10); - drawText("0005", Color.BLACK, new Point(170, 80), 10); - drawText("0004", Color.BLACK, new Point(210, 80), 10); - drawText("0003", Color.BLACK, new Point(250, 80), 10); - drawText("0002", Color.BLACK, new Point(290, 80), 10); - drawText("0001", Color.BLACK, new Point(330, 80), 10); - drawText("0000", Color.BLACK, new Point(370, 80), 10); + drawText("0007", Colors.FOREGROUND, new Point(90, 80), 10); + drawText("0006", Colors.FOREGROUND, new Point(130, 80), 10); + drawText("0005", Colors.FOREGROUND, new Point(170, 80), 10); + drawText("0004", Colors.FOREGROUND, new Point(210, 80), 10); + drawText("0003", Colors.FOREGROUND, new Point(250, 80), 10); + drawText("0002", Colors.FOREGROUND, new Point(290, 80), 10); + drawText("0001", Colors.FOREGROUND, new Point(330, 80), 10); + drawText("0000", Colors.FOREGROUND, new Point(370, 80), 10); - drawText("000f", Color.BLACK, new Point(90, 130), 10); - drawText("000e", Color.BLACK, new Point(130, 130), 10); - drawText("000d", Color.BLACK, new Point(170, 130), 10); - drawText("000c", Color.BLACK, new Point(210, 130), 10); - drawText("000b", Color.BLACK, new Point(250, 130), 10); - drawText("000a", Color.BLACK, new Point(290, 130), 10); - drawText("0009", Color.BLACK, new Point(330, 130), 10); - drawText("0008", Color.BLACK, new Point(370, 130), 10); + drawText("000f", Colors.FOREGROUND, new Point(90, 130), 10); + drawText("000e", Colors.FOREGROUND, new Point(130, 130), 10); + drawText("000d", Colors.FOREGROUND, new Point(170, 130), 10); + drawText("000c", Colors.FOREGROUND, new Point(210, 130), 10); + drawText("000b", Colors.FOREGROUND, new Point(250, 130), 10); + drawText("000a", Colors.FOREGROUND, new Point(290, 130), 10); + drawText("0009", Colors.FOREGROUND, new Point(330, 130), 10); + drawText("0008", Colors.FOREGROUND, new Point(370, 130), 10); //Draw boxes Point p1 = new Point(80, 65); @@ -106,24 +109,24 @@ public class MemoryMapPluginScreenShots extends GhidraScreenShotGenerator { Point p7 = new Point(400, 140); Point p8 = new Point(80, 140); - drawLine(Color.BLACK, 1, p1, p2); - drawLine(Color.BLACK, 1, p2, p3); - drawLine(Color.BLACK, 1, p3, p4); - drawLine(Color.BLACK, 1, p4, p1); + drawLine(Palette.BLACK, 1, p1, p2); + drawLine(Palette.BLACK, 1, p2, p3); + drawLine(Palette.BLACK, 1, p3, p4); + drawLine(Palette.BLACK, 1, p4, p1); - drawLine(Color.BLACK, 1, p5, p6); - drawLine(Color.BLACK, 1, p6, p7); - drawLine(Color.BLACK, 1, p7, p8); - drawLine(Color.BLACK, 1, p8, p5); + drawLine(Palette.BLACK, 1, p5, p6); + drawLine(Palette.BLACK, 1, p6, p7); + drawLine(Palette.BLACK, 1, p7, p8); + drawLine(Palette.BLACK, 1, p8, p5); for (int i = 1; i < 8; i++) { - drawLine(Color.BLACK, 1, new Point(80 + i * 40, 65), new Point(80 + i * 40, 90)); - drawLine(Color.BLACK, 1, new Point(80 + i * 40, 115), new Point(80 + i * 40, 140)); + drawLine(Palette.BLACK, 1, new Point(80 + i * 40, 65), new Point(80 + i * 40, 90)); + drawLine(Palette.BLACK, 1, new Point(80 + i * 40, 115), new Point(80 + i * 40, 140)); } } -@Test - public void testAddMappedBlock() { + @Test + public void testAddMappedBlock() { performAction("Add Block", "MemoryMapPlugin", false); @@ -133,11 +136,11 @@ public class MemoryMapPluginScreenShots extends GhidraScreenShotGenerator { captureDialog(); - drawRectangleAround(comboBox, Color.GREEN, 10); + drawRectangleAround(comboBox, Palette.GREEN, 10); } -@Test - public void testMoveMemory() { + @Test + public void testMoveMemory() { performAction("Memory Map", "DockingWindows", true); @@ -155,8 +158,8 @@ public class MemoryMapPluginScreenShots extends GhidraScreenShotGenerator { captureDialog(); } -@Test - public void testSplitMemoryBlock() { + @Test + public void testSplitMemoryBlock() { performAction("Memory Map", "DockingWindows", true); @@ -174,8 +177,8 @@ public class MemoryMapPluginScreenShots extends GhidraScreenShotGenerator { captureDialog(); } -@Test - public void testMemoryExpandUp() { + @Test + public void testMemoryExpandUp() { performAction("Memory Map", "DockingWindows", true); @@ -193,8 +196,8 @@ public class MemoryMapPluginScreenShots extends GhidraScreenShotGenerator { captureDialog(); } -@Test - public void testMemoryExpandDown() { + @Test + public void testMemoryExpandDown() { performAction("Memory Map", "DockingWindows", true); @@ -212,8 +215,8 @@ public class MemoryMapPluginScreenShots extends GhidraScreenShotGenerator { captureDialog(); } -@Test - public void testSetImageBaseDialog() { + @Test + public void testSetImageBaseDialog() { performAction("Memory Map", "DockingWindows", true); @@ -234,55 +237,48 @@ public class MemoryMapPluginScreenShots extends GhidraScreenShotGenerator { private void selectRow(final GhidraTable table, final String text) { final TableModel model = table.getModel(); - runSwing(new Runnable() { - - @Override - public void run() { - int columnCount = model.getColumnCount(); - int columnIndex = -1; - int rowIndex = -1; - for (int i = 0; i < columnCount; i++) { - if (model.getColumnName(i).equals("Name")) { - columnIndex = i; + runSwing(() -> { + int columnCount = model.getColumnCount(); + int columnIndex = -1; + int rowIndex = -1; + for (int i = 0; i < columnCount; i++) { + if (model.getColumnName(i).equals("Name")) { + columnIndex = i; + break; + } + } + if (columnIndex != -1) { + int rowCount = model.getRowCount(); + for (int i = 0; i < rowCount; i++) { + if (model.getValueAt(i, columnIndex).equals(text)) { + rowIndex = i; break; } } - if (columnIndex != -1) { - int rowCount = model.getRowCount(); - for (int i = 0; i < rowCount; i++) { - if (model.getValueAt(i, columnIndex).equals(text)) { - rowIndex = i; - break; - } - } - } - if (rowIndex == -1) { - throw new AssertException(); - } - table.selectRow(rowIndex); - } + if (rowIndex == -1) { + throw new AssertException(); + } + table.selectRow(rowIndex); + }); } private void selectItem(final GhidraComboBox comboBox, final String text) { - runSwing(new Runnable() { - @Override - public void run() { - int itemCount = comboBox.getItemCount(); - Object item = null; - for (int i = 0; i < itemCount; i++) { - Object itemAt = comboBox.getItemAt(i); - if (itemAt.toString().equals(text)) { - item = itemAt; - break; - } + runSwing(() -> { + int itemCount = comboBox.getItemCount(); + Object item = null; + for (int i = 0; i < itemCount; i++) { + Object itemAt = comboBox.getItemAt(i); + if (itemAt.toString().equals(text)) { + item = itemAt; + break; } - if (item == null) { - throw new AssertException(); - } - comboBox.setSelectedItem(item); } + if (item == null) { + throw new AssertException(); + } + comboBox.setSelectedItem(item); }); } } diff --git a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/MemorySearchScreenShots.java b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/MemorySearchScreenShots.java index 5911fefa89..800d162147 100644 --- a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/MemorySearchScreenShots.java +++ b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/MemorySearchScreenShots.java @@ -24,6 +24,7 @@ import org.junit.Test; import docking.DialogComponentProvider; import docking.action.DockingActionIf; +import generic.theme.GThemeDefaults.Colors.Palette; import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin; import ghidra.app.plugin.core.codebrowser.CodeViewerProvider; import ghidra.app.plugin.core.searchmem.mask.MnemonicSearchPlugin; @@ -168,9 +169,9 @@ public class MemorySearchScreenShots extends AbstractSearchScreenShots { @Test public void testSearchInstructions() { Font font = new Font("Monospaced", Font.PLAIN, 14); - Color selectionColor = new Color(180, 255, 180); + Color selectionColor = Palette.getColor("palegreen"); TextFormatter tf = new TextFormatter(font, 8, 500, 4, 5, 2); - TextFormatterContext blue = new TextFormatterContext(Color.BLUE); + TextFormatterContext blue = new TextFormatterContext(Palette.BLUE); TextFormatterContext darkBlue = new TextFormatterContext(DARK_BLUE); TextFormatterContext darkGreen = new TextFormatterContext(DARK_GREEN); TextFormatterContext orange = new TextFormatterContext(YELLOW_ORANGE); @@ -194,7 +195,7 @@ public class MemorySearchScreenShots extends AbstractSearchScreenShots { public void testSearchInstructionsIncludeOperands() { Font font = new Font("Monospaced", Font.PLAIN, 14); TextFormatter tf = new TextFormatter(font, 4, 300, 4, 5, 2); - TextFormatterContext blue = new TextFormatterContext(Color.BLUE); + TextFormatterContext blue = new TextFormatterContext(Palette.BLUE); TextFormatterContext darkBlue = new TextFormatterContext(DARK_BLUE); TextFormatterContext darkGreen = new TextFormatterContext(DARK_GREEN); TextFormatterContext orange = new TextFormatterContext(YELLOW_ORANGE); @@ -258,7 +259,7 @@ public class MemorySearchScreenShots extends AbstractSearchScreenShots { performAction(action, provider, false); // And capture the error dialog. - Window errorDialog = waitForWindow("Mnemonic Search Error", 2000); + Window errorDialog = waitForWindow("Mnemonic Search Error"); captureWindow(errorDialog); } } diff --git a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/OverviewPluginScreenShots.java b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/OverviewPluginScreenShots.java index 6503c9df64..7b03cd72bd 100644 --- a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/OverviewPluginScreenShots.java +++ b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/OverviewPluginScreenShots.java @@ -19,6 +19,8 @@ import java.awt.*; import org.junit.Test; +import generic.theme.GThemeDefaults.Colors; +import generic.theme.GThemeDefaults.Colors.Palette; import ghidra.app.plugin.core.codebrowser.CodeViewerProvider; import ghidra.app.plugin.core.overview.OverviewColorLegendDialog; import ghidra.app.plugin.core.overview.OverviewColorPlugin; @@ -54,9 +56,9 @@ public class OverviewPluginScreenShots extends GhidraScreenShotGenerator { }); captureIsolatedProvider(CodeViewerProvider.class, 700, 400); - padImage(new Color(0, 0, 0, 0), 10, 0, 50, 0); - drawOval(Color.RED, new Rectangle(630, 2, 40, 40), 3); - drawOval(Color.RED, new Rectangle(668, 55, 40, 240), 3); + padImage(Palette.NO_COLOR, 10, 0, 50, 0); + drawOval(Palette.RED, new Rectangle(630, 2, 40, 40), 3); + drawOval(Palette.RED, new Rectangle(668, 55, 40, 240), 3); } @Test @@ -126,18 +128,18 @@ public class OverviewPluginScreenShots extends GhidraScreenShotGenerator { image = createEmptyImage(width + margin * 2, height + margin * 2); Point p = new Point(margin, margin + smallFontHeight); - drawText(sumTop, Color.BLACK, p, smallFont); + drawText(sumTop, Colors.FOREGROUND, p, smallFont); p.y += bigMetrics.getAscent() - bigMetrics.getDescent() / 2; - drawText(sum, Color.BLACK, p, bigFont); + drawText(sum, Colors.FOREGROUND, p, bigFont); p.y += smallFontHeight; - drawText(sumBottom, Color.BLACK, p, smallFont); + drawText(sumBottom, Colors.FOREGROUND, p, smallFont); p.x += bigMetrics.stringWidth(sum); p.y = margin + smallFontHeight + bigFontHeight / 2 + metrics.getHeight() / 2 - bigMetrics.getDescent() / 2; - drawText(equation, Color.BLACK, p, mediumFont); + drawText(equation, Colors.FOREGROUND, p, mediumFont); } diff --git a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/ProgramGraphPluginScreenShots.java b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/ProgramGraphPluginScreenShots.java index f05807334b..ab457a9702 100644 --- a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/ProgramGraphPluginScreenShots.java +++ b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/ProgramGraphPluginScreenShots.java @@ -15,11 +15,14 @@ */ package help.screenshot; -import java.awt.*; +import java.awt.Point; +import java.awt.Rectangle; import org.junit.Test; import docking.action.DockingActionIf; +import generic.theme.GThemeDefaults.Colors; +import generic.theme.GThemeDefaults.Colors.Palette; import ghidra.app.util.viewer.field.*; import ghidra.graph.visualization.DefaultGraphDisplayComponentProvider; @@ -110,9 +113,10 @@ public class ProgramGraphPluginScreenShots extends GhidraScreenShotGenerator { Point p2 = new Point(STARTX + WIDTH, startY); Point p3 = new Point(STARTX, endY); Point p4 = new Point(STARTX + WIDTH, endY); - drawLine(Color.BLACK, 3, p1, p2); - drawLine(Color.BLACK, 3, p2, p4); - drawLine(Color.BLACK, 3, p3, p4); - drawText(string, Color.BLACK, new Point(STARTX + WIDTH + 10, (startY + endY) / 2), 12); + drawLine(Palette.BLACK, 3, p1, p2); + drawLine(Palette.BLACK, 3, p2, p4); + drawLine(Palette.BLACK, 3, p3, p4); + drawText(string, Colors.FOREGROUND, new Point(STARTX + WIDTH + 10, (startY + endY) / 2), + 12); } } diff --git a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/ProgramManagerPluginScreenShots.java b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/ProgramManagerPluginScreenShots.java index 26dd3b18f9..0593c26f14 100644 --- a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/ProgramManagerPluginScreenShots.java +++ b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/ProgramManagerPluginScreenShots.java @@ -34,6 +34,7 @@ import docking.options.editor.OptionsDialog; import docking.widgets.OptionDialog; import docking.widgets.tree.GTree; import docking.widgets.tree.GTreeNode; +import generic.theme.GThemeDefaults.Colors.Palette; import ghidra.app.plugin.core.codebrowser.CodeViewerProvider; import ghidra.app.plugin.core.datamgr.DataTypesProvider; import ghidra.app.plugin.core.progmgr.MultiTabPlugin; @@ -49,7 +50,7 @@ import ghidra.program.model.listing.Program; import ghidra.program.model.symbol.SourceType; import ghidra.util.exception.CancelledException; import ghidra.util.task.TaskLauncher; -import ghidra.util.task.TaskMonitorAdapter; +import ghidra.util.task.TaskMonitor; public class ProgramManagerPluginScreenShots extends GhidraScreenShotGenerator implements CheckinHandler { @@ -65,7 +66,7 @@ public class ProgramManagerPluginScreenShots extends GhidraScreenShotGenerator createAndOpenPrograms(2, 1); closeProvider(DataTypesProvider.class); captureToolWindow(900, 400); - drawOval(new Color(108, 0, 0), new Rectangle(280, 92, 190, 60), 8); + drawOval(Palette.PURPLE, new Rectangle(280, 92, 190, 60), 8); } @@ -113,7 +114,7 @@ public class ProgramManagerPluginScreenShots extends GhidraScreenShotGenerator ProjectData projectData = project.getProjectData(); projectData.getRootFolder() .createFile("WinHelloCpp.exe", program, - TaskMonitorAdapter.DUMMY_MONITOR); + TaskMonitor.DUMMY); DomainFile df = program.getDomainFile(); @@ -146,7 +147,7 @@ public class ProgramManagerPluginScreenShots extends GhidraScreenShotGenerator runSwing(() -> invokeInstanceMethod("advancedButtonCallback", dialog)); captureDialog(850, 400); - closeAllWindowsAndFrames(); + closeAllWindows(); } @Test @@ -156,7 +157,7 @@ public class ProgramManagerPluginScreenShots extends GhidraScreenShotGenerator performAction("Open File", "ProgramManagerPlugin", false); captureDialog(500, 400); - closeAllWindowsAndFrames(); + closeAllWindows(); } @@ -176,7 +177,7 @@ public class ProgramManagerPluginScreenShots extends GhidraScreenShotGenerator rightClick(jTree, rowBounds.x + 25, rowBounds.y + 10); waitForSwing(); captureDialog(); - closeAllWindowsAndFrames(); + closeAllWindows(); } @Test @@ -231,7 +232,7 @@ public class ProgramManagerPluginScreenShots extends GhidraScreenShotGenerator waitForSwing(); createAndOpenPrograms(6, 3); captureProvider(CodeViewerProvider.class); - drawOval(new Color(108, 0, 0), new Rectangle(440, 16, 70, 50), 8); + drawOval(Palette.PURPLE, new Rectangle(440, 16, 70, 50), 8); } @Test @@ -273,7 +274,7 @@ public class ProgramManagerPluginScreenShots extends GhidraScreenShotGenerator }); captureProvider(CodeViewerProvider.class); - drawOval(new Color(108, 0, 0), new Rectangle(221, 16, 140, 50), 8); + drawOval(Palette.PURPLE, new Rectangle(221, 16, 140, 50), 8); } @Test @@ -310,7 +311,7 @@ public class ProgramManagerPluginScreenShots extends GhidraScreenShotGenerator String programName = "Program" + (i + 1) + ".exe"; list.add(projectData.getRootFolder() .createFile(programName, program, - TaskMonitorAdapter.DUMMY_MONITOR)); + TaskMonitor.DUMMY)); } program.flushEvents(); @@ -350,7 +351,7 @@ public class ProgramManagerPluginScreenShots extends GhidraScreenShotGenerator TaskLauncher.launchModal(comment, () -> { try { domainFile.addToVersionControl(comment, keepItCheckedOut, - TaskMonitorAdapter.DUMMY_MONITOR); + TaskMonitor.DUMMY); } catch (CancelledException | IOException e) { throw new RuntimeException(e); @@ -365,17 +366,11 @@ public class ProgramManagerPluginScreenShots extends GhidraScreenShotGenerator return space.getAddress(offset); } - /* - * @see ghidra.framework.data.CheckinHandler#getComment() - */ @Override public String getComment() { return checkinComment; } - /* - * @see ghidra.framework.data.CheckinHandler#keepCheckedOut() - */ @Override public boolean keepCheckedOut() { return keepCheckedOut; diff --git a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/ReferencesPluginScreenShots.java b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/ReferencesPluginScreenShots.java index d351aeb5a7..41d1476bfd 100644 --- a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/ReferencesPluginScreenShots.java +++ b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/ReferencesPluginScreenShots.java @@ -25,6 +25,8 @@ import javax.swing.*; import org.junit.Test; +import generic.theme.GThemeDefaults.Colors; +import generic.theme.GThemeDefaults.Colors.Palette; import ghidra.app.plugin.core.codebrowser.CodeViewerProvider; import ghidra.app.plugin.core.references.*; import ghidra.app.util.importer.*; @@ -84,19 +86,19 @@ public class ReferencesPluginScreenShots extends GhidraScreenShotGenerator { captureProvider(provider); int topMargin = 60; int leftMargin = 10; - padImage(Color.WHITE, topMargin, leftMargin, 10, 10); + padImage(Colors.BACKGROUND, topMargin, leftMargin, 10, 10); JComponent rootComp = getDockableComponent(EditReferencesProvider.class); JComponent comp = (JComponent) findComponentByName(provider.getComponent(), "operandLabels[0]"); Point origin = new Point(leftMargin, topMargin); - explainComponent(rootComp, comp, Color.GREEN, origin, new Point(250, 20), + explainComponent(rootComp, comp, Palette.GREEN, origin, new Point(250, 20), "Operand-specific Drop Zones"); comp = (JComponent) findComponentByName(provider.getComponent(), "mnemonicLabel"); - explainComponent(rootComp, comp, Color.GREEN, origin, new Point(250, 20), + explainComponent(rootComp, comp, Palette.GREEN, origin, new Point(250, 20), "Operand-specific Drop Zones"); comp = (JComponent) findComponentByName(provider.getComponent(), "RefsTable"); - explainComponent(rootComp, comp, Color.GREEN, origin, new Point(450, 40), + explainComponent(rootComp, comp, Palette.GREEN, origin, new Point(450, 40), "Active-operand Drop Zones"); } @@ -113,7 +115,7 @@ public class ReferencesPluginScreenShots extends GhidraScreenShotGenerator { Point p2 = new Point(point.x + 20, point.y); drawLine(color, 2, point, p2); Point p3 = new Point(p2.x + 4, p2.y + 5); - drawText(text, Color.BLACK, p3, 12f); + drawText(text, Colors.FOREGROUND, p3, 12f); } @Test @@ -270,7 +272,7 @@ public class ReferencesPluginScreenShots extends GhidraScreenShotGenerator { RenderingHints.VALUE_TEXT_ANTIALIAS_ON); g2.drawImage(image1, 0, 0, null); g2.drawImage(image2, 0, height + gap, null); - g2.setColor(Color.BLACK); + g2.setColor(Colors.FOREGROUND); String label = "Address History"; int x = 150; int y = height + gap / 2; @@ -293,8 +295,11 @@ public class ReferencesPluginScreenShots extends GhidraScreenShotGenerator { programNameOverride, OptionChooser.DEFAULT_OPTIONS, MultipleProgramsStrategy.ALL_PROGRAMS); Program p = programs.get(0); - env.getProject().getProjectData().getRootFolder().createFile(p.getName(), p, - TaskMonitor.DUMMY); + env.getProject() + .getProjectData() + .getRootFolder() + .createFile(p.getName(), p, + TaskMonitor.DUMMY); } } diff --git a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/RepositoryCustomScreenShots.java b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/RepositoryCustomScreenShots.java index 8ff6735d58..f198fbac9a 100644 --- a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/RepositoryCustomScreenShots.java +++ b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/RepositoryCustomScreenShots.java @@ -15,7 +15,7 @@ */ package help.screenshot; -import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.*; import java.awt.*; import java.awt.image.BufferedImage; @@ -25,6 +25,7 @@ import javax.swing.*; import org.junit.Test; +import generic.theme.GThemeDefaults.Colors.Palette; import ghidra.app.merge.MergeProgressPanel; import ghidra.util.layout.VerticalLayout; import resources.ResourceManager; @@ -38,8 +39,8 @@ public class RepositoryCustomScreenShots extends GhidraScreenShotGenerator { @Test public void testMultiUser() { image = createEmptyImage(800, 600); - Color purple = new Color(255, 0, 255); - Color green = new Color(100, 255, 100); + Color purple = Palette.getColor("fuchsia"); + Color green = Palette.LIME; int y = 50; int x = 450; int spacing = 100; @@ -69,12 +70,12 @@ public class RepositoryCustomScreenShots extends GhidraScreenShotGenerator { Point p_leg3_text = new Point(p_leg3.x + indent, p_leg3.y); drawLine(purple, p_v0, p_v3, false); - drawLine(Color.BLACK, p_v1, p_co_v1, true); - drawLine(Color.BLACK, p_v1, p_co_v2, true); + drawLine(Palette.BLACK, p_v1, p_co_v1, true); + drawLine(Palette.BLACK, p_v1, p_co_v2, true); drawLine(green, p_co_v1, p_box1, false); drawLine(green, p_box1, p_box2, false); - drawLine(Color.BLUE, p_co_v2, p_v2, false); - drawLine(Color.BLUE, p_box2, p_v3, false); + drawLine(Palette.BLUE, p_co_v2, p_v2, false); + drawLine(Palette.BLUE, p_box2, p_v3, false); drawBubble("Version 0", null, p_v0); drawBubble("Version 1", null, p_v1); @@ -98,9 +99,9 @@ public class RepositoryCustomScreenShots extends GhidraScreenShotGenerator { drawText(p_leg1_text, -1, " Check Out"); drawText(p_leg2_text, -1, " Make changes and merge"); drawText(p_leg3_text, -1, " Create new version"); - drawLine(Color.BLACK, p_leg1, p_leg1_text, true); + drawLine(Palette.BLACK, p_leg1, p_leg1_text, true); drawLine(green, p_leg2, p_leg2_text, false); - drawLine(Color.BLUE, p_leg3, p_leg3_text, false); + drawLine(Palette.BLUE, p_leg3, p_leg3_text, false); } private void drawLine(Color color, Point p1, Point p2, boolean dashed) { @@ -130,7 +131,7 @@ public class RepositoryCustomScreenShots extends GhidraScreenShotGenerator { g.setFont(f); FontMetrics metrics = g.getFontMetrics(); int height = metrics.getHeight(); - g.setColor(Color.BLACK); + g.setColor(Palette.BLACK); g.drawOval(x, y, radius * 2, radius * 2); if (text2 == null) { @@ -168,7 +169,7 @@ public class RepositoryCustomScreenShots extends GhidraScreenShotGenerator { int y = p.y - height / 2; g.fillRect(x, y, width, height); - g.setColor(Color.BLACK); + g.setColor(Palette.BLACK); g.drawRect(x, y, width, height); y += margin; @@ -203,7 +204,7 @@ public class RepositoryCustomScreenShots extends GhidraScreenShotGenerator { // int y = p.y - height / 2; // g.fillRect(x, y, width, height); - g.setColor(Color.BLACK); + g.setColor(Palette.BLACK); int y = p.y - height / 2; int x = p.x; @@ -221,7 +222,7 @@ public class RepositoryCustomScreenShots extends GhidraScreenShotGenerator { @Test public void testAutoMergeCodeUnits() { - closeAllWindowsAndFrames(); + closeAllWindows(); final MergeProgressPanel panel = new MergeProgressPanel(); final String[] MEMORY = new String[] { "Memory" }; @@ -249,18 +250,14 @@ public class RepositoryCustomScreenShots extends GhidraScreenShotGenerator { panel.addInfo(EXTERNAL_PROGRAM); panel.addInfo(PROPERTY_LIST); - runSwing(new Runnable() { - - @Override - public void run() { - panel.setCompleted(MEMORY); - panel.setCompleted(PROGRAM_TREE); - panel.setCompleted(DATA_TYPES); - panel.setCompleted(PROGRAM_CONTEXT); - panel.setCompleted(LISTING); - panel.setCompleted(BYTES); - panel.setInProgress(FUNCTIONS); - } + runSwing(() -> { + panel.setCompleted(MEMORY); + panel.setCompleted(PROGRAM_TREE); + panel.setCompleted(DATA_TYPES); + panel.setCompleted(PROGRAM_CONTEXT); + panel.setCompleted(LISTING); + panel.setCompleted(BYTES); + panel.setInProgress(FUNCTIONS); }); JPanel mainPanel = new JPanel(new VerticalLayout(20)); diff --git a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/RepositoryScreenShots.java b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/RepositoryScreenShots.java index b818842fc7..e0ae764585 100644 --- a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/RepositoryScreenShots.java +++ b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/RepositoryScreenShots.java @@ -28,6 +28,7 @@ import org.junit.*; import docking.widgets.indexedscrollpane.IndexedScrollPane; import generic.test.TestUtils; +import generic.theme.GThemeDefaults.Colors.Palette; import ghidra.app.cmd.function.AddRegisterParameterCommand; import ghidra.app.merge.*; import ghidra.app.merge.listing.*; @@ -71,7 +72,8 @@ public class RepositoryScreenShots extends AbstractListingMergeManagerTest { public void setUp() throws Exception { super.setUp(); String testFilename = getClass().getSimpleName().replace(".class", ""); - mtfGenerator = new MergeScreenShotGenerator(testFilename, testName.getMethodName(), mtf, testName); + mtfGenerator = + new MergeScreenShotGenerator(testFilename, testName.getMethodName(), mtf, testName); env = mtf.getTestEnvironment(); } @@ -150,7 +152,7 @@ public class RepositoryScreenShots extends AbstractListingMergeManagerTest { Container namePanel = (Container) TestUtils.getInstanceField("namePanel", memoryMergePanel); setToolSize(600, 450); Window window = mergeTool.getActiveWindow(); - + mtfGenerator.captureComponent(window); // chooseRadioButton(MergeConstants.MY_TITLE, namePanel.getClass(), false); @@ -642,13 +644,13 @@ public class RepositoryScreenShots extends AbstractListingMergeManagerTest { int arrowHead2Y = lock3Y; int description1Y = arrowTailY - 10; int description2Y = arrowTailY + 10; - mtfGenerator.drawArrow(Color.RED, 2, new Point(arrowTailX, arrowTailY), + mtfGenerator.drawArrow(Palette.RED, 2, new Point(arrowTailX, arrowTailY), new Point(arrowHeadX, arrowHead1Y), 8); - mtfGenerator.drawArrow(Color.RED, 2, new Point(arrowTailX, arrowTailY), + mtfGenerator.drawArrow(Palette.RED, 2, new Point(arrowTailX, arrowTailY), new Point(arrowHeadX, arrowHead2Y), 8); - mtfGenerator.drawText(description1, Color.RED, new Point(descriptionX, description1Y), + mtfGenerator.drawText(description1, Palette.RED, new Point(descriptionX, description1Y), DESCRIPTION_FONT_SIZE); - mtfGenerator.drawText(description2, Color.RED, new Point(descriptionX, description2Y), + mtfGenerator.drawText(description2, Palette.RED, new Point(descriptionX, description2Y), DESCRIPTION_FONT_SIZE); } @@ -661,7 +663,7 @@ public class RepositoryScreenShots extends AbstractListingMergeManagerTest { Dimension size = listingPanels[0].getSize(); int descriptionX = listingX + (size.width / 3); int descriptionY = listingY + size.height - 20; - mtfGenerator.drawText(greyListingDescription, Color.RED, + mtfGenerator.drawText(greyListingDescription, Palette.RED, new Point(descriptionX, descriptionY), DESCRIPTION_FONT_SIZE); } @@ -690,9 +692,9 @@ public class RepositoryScreenShots extends AbstractListingMergeManagerTest { int arrowHeadY = buttonY; int arrowTailY = buttonY; int descriptionY = buttonY + 5; - mtfGenerator.drawArrow(Color.RED, 2, new Point(arrowTailX, arrowTailY), + mtfGenerator.drawArrow(Palette.RED, 2, new Point(arrowTailX, arrowTailY), new Point(arrowHeadX, arrowHeadY), 8); - mtfGenerator.drawText(adjustFieldsDescription, Color.RED, + mtfGenerator.drawText(adjustFieldsDescription, Palette.RED, new Point(descriptionX, descriptionY), DESCRIPTION_FONT_SIZE); } @@ -711,11 +713,11 @@ public class RepositoryScreenShots extends AbstractListingMergeManagerTest { int arrowHeadY = westLabelBottomY; int arrowTailY = westLabelBottomY + 15; int descriptionY = westLabelBottomY + 20; - mtfGenerator.drawArrow(Color.RED, 2, new Point(arrowTailX, arrowTailY), + mtfGenerator.drawArrow(Palette.RED, 2, new Point(arrowTailX, arrowTailY), new Point(arrowHeadX, arrowTailY), 0); - mtfGenerator.drawArrow(Color.RED, 2, new Point(arrowHeadX, arrowTailY), + mtfGenerator.drawArrow(Palette.RED, 2, new Point(arrowHeadX, arrowTailY), new Point(arrowHeadX, arrowHeadY), 8); - mtfGenerator.drawText("Indicates which conflict you are resolving.", Color.RED, + mtfGenerator.drawText("Indicates which conflict you are resolving.", Palette.RED, new Point(descriptionX, descriptionY), DESCRIPTION_FONT_SIZE); } @@ -734,9 +736,9 @@ public class RepositoryScreenShots extends AbstractListingMergeManagerTest { int arrowHeadY = eastLabelY - 10; int arrowTailY = arrowHeadY - 30; int descriptionY = arrowTailY - 5; - mtfGenerator.drawArrow(Color.RED, 2, new Point(arrowHeadX, arrowTailY), + mtfGenerator.drawArrow(Palette.RED, 2, new Point(arrowHeadX, arrowTailY), new Point(arrowHeadX, arrowHeadY), 8); - mtfGenerator.drawText(addressRangeDescription, Color.RED, + mtfGenerator.drawText(addressRangeDescription, Palette.RED, new Point(descriptionX, descriptionY), DESCRIPTION_FONT_SIZE); } @@ -744,7 +746,7 @@ public class RepositoryScreenShots extends AbstractListingMergeManagerTest { Graphics g = image.getGraphics(); Font font = g.getFont(); g.setFont(font.deriveFont(DESCRIPTION_FONT_SIZE)); - g.setColor(Color.RED); + g.setColor(Palette.RED); FontMetrics fontMetrics = g.getFontMetrics(font.deriveFont(DESCRIPTION_FONT_SIZE)); int descriptionWidth = fontMetrics.stringWidth(description); return descriptionWidth; @@ -763,9 +765,9 @@ public class RepositoryScreenShots extends AbstractListingMergeManagerTest { int westLabelY = westLabelLocation.y - windowLocationOnScreen.y; int arrowY = westLabelY + 5; int descriptionY = arrowY; - mtfGenerator.drawArrow(Color.RED, 2, new Point(arrowTailX, arrowY), + mtfGenerator.drawArrow(Palette.RED, 2, new Point(arrowTailX, arrowY), new Point(arrowHeadX, arrowY), 8); - mtfGenerator.drawText("Indicates the address(es) in conflict.", Color.RED, + mtfGenerator.drawText("Indicates the address(es) in conflict.", Palette.RED, new Point(descriptionX, descriptionY), DESCRIPTION_FONT_SIZE); } @@ -783,9 +785,9 @@ public class RepositoryScreenShots extends AbstractListingMergeManagerTest { int arrowTailY = endTitleTextY - 5; int arrowHeadY = endTitleTextY + 5; int descriptionY = endTitleTextY; - mtfGenerator.drawArrow(Color.RED, 2, new Point(arrowTailX, arrowTailY), + mtfGenerator.drawArrow(Palette.RED, 2, new Point(arrowTailX, arrowTailY), new Point(arrowHeadX, arrowHeadY), 8); - mtfGenerator.drawText("Type of conflict to resolve.", Color.RED, + mtfGenerator.drawText("Type of conflict to resolve.", Palette.RED, new Point(descriptionX, descriptionY), DESCRIPTION_FONT_SIZE); } @@ -794,9 +796,9 @@ public class RepositoryScreenShots extends AbstractListingMergeManagerTest { Point variousChoicesLocation = variousChoicesPanel.getLocationOnScreen(); int conflictAreaX = variousChoicesLocation.x - windowLocationOnScreen.x; int conflictAreaY = variousChoicesLocation.y - windowLocationOnScreen.y; - mtfGenerator.drawText("Area indicating the specific information", Color.RED, + mtfGenerator.drawText("Area indicating the specific information", Palette.RED, new Point(conflictAreaX + 20, conflictAreaY + 40), DESCRIPTION_FONT_SIZE); - mtfGenerator.drawText("for the current conflict(s) being resolved.", Color.RED, + mtfGenerator.drawText("for the current conflict(s) being resolved.", Palette.RED, new Point(conflictAreaX + 20, conflictAreaY + 60), DESCRIPTION_FONT_SIZE); } @@ -816,11 +818,11 @@ public class RepositoryScreenShots extends AbstractListingMergeManagerTest { int arrowTailY = radioButtonY - 10; int description1Y = radioButtonY - 20; int description2Y = radioButtonY; - mtfGenerator.drawArrow(Color.RED, 2, new Point(arrowTailX, arrowTailY), + mtfGenerator.drawArrow(Palette.RED, 2, new Point(arrowTailX, arrowTailY), new Point(arrowHeadX, arrowHeadY), 8); - mtfGenerator.drawText("Radio buttons or check boxes to", Color.RED, + mtfGenerator.drawText("Radio buttons or check boxes to", Palette.RED, new Point(descriptionX, description1Y), DESCRIPTION_FONT_SIZE); - mtfGenerator.drawText("select for resolving the conflict(s).", Color.RED, + mtfGenerator.drawText("select for resolving the conflict(s).", Palette.RED, new Point(descriptionX, description2Y), DESCRIPTION_FONT_SIZE); } @@ -838,11 +840,11 @@ public class RepositoryScreenShots extends AbstractListingMergeManagerTest { int checkBoxHeight = useForAllCB.getSize().height; int arrowY = useForAllY + (checkBoxHeight / 2); int descriptionY = useForAllY + 15; - mtfGenerator.drawArrow(Color.RED, 2, new Point(arrowTailX, arrowY), + mtfGenerator.drawArrow(Palette.RED, 2, new Point(arrowTailX, arrowY), new Point(arrowHeadX, arrowY), 8); mtfGenerator.drawText( "Check this box to automatically make the same choice for all remaining conflicts like this one.", - Color.RED, new Point(descriptionX, descriptionY), DESCRIPTION_FONT_SIZE); + Palette.RED, new Point(descriptionX, descriptionY), DESCRIPTION_FONT_SIZE); } private void addApplyDescription(Image image, Point windowLocationOnScreen, @@ -863,11 +865,11 @@ public class RepositoryScreenShots extends AbstractListingMergeManagerTest { int arrowHeadY = applyButtonY + 10; int description1Y = applyButtonY + 5; int description2Y = description1Y + 20; - mtfGenerator.drawArrow(Color.RED, 2, new Point(arrowTailX, arrowTailY), + mtfGenerator.drawArrow(Palette.RED, 2, new Point(arrowTailX, arrowTailY), new Point(arrowHeadX, arrowHeadY), 8); - mtfGenerator.drawText(description1, Color.RED, new Point(descriptionX, description1Y), + mtfGenerator.drawText(description1, Palette.RED, new Point(descriptionX, description1Y), DESCRIPTION_FONT_SIZE); - mtfGenerator.drawText(description2, Color.RED, new Point(descriptionX, description2Y), + mtfGenerator.drawText(description2, Palette.RED, new Point(descriptionX, description2Y), DESCRIPTION_FONT_SIZE); } @@ -2409,8 +2411,9 @@ public class RepositoryScreenShots extends AbstractListingMergeManagerTest { int txId = program.startTransaction("Modify Original Program"); boolean commit = false; try { - program.getExternalManager().setExternalPath("ADVAPI32.DLL", "//advapi32.dll", - true); + program.getExternalManager() + .setExternalPath("ADVAPI32.DLL", "//advapi32.dll", + true); commit = true; } finally { @@ -2465,8 +2468,9 @@ public class RepositoryScreenShots extends AbstractListingMergeManagerTest { int txId = program.startTransaction("Modify Original Program"); boolean commit = false; try { - program.getExternalManager().setExternalPath("ADVAPI32.DLL", "//advapi32.dll", - true); + program.getExternalManager() + .setExternalPath("ADVAPI32.DLL", "//advapi32.dll", + true); commit = true; } finally { diff --git a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/ShowInstructionInfoPluginScreenShots.java b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/ShowInstructionInfoPluginScreenShots.java index e6a485404f..ed26a0326e 100644 --- a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/ShowInstructionInfoPluginScreenShots.java +++ b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/ShowInstructionInfoPluginScreenShots.java @@ -27,6 +27,7 @@ import docking.*; import docking.widgets.fieldpanel.FieldPanel; import docking.widgets.fieldpanel.Layout; import docking.widgets.fieldpanel.field.Field; +import generic.theme.GThemeDefaults.Colors.Palette; import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin; import ghidra.app.plugin.core.codebrowser.CodeViewerProvider; import ghidra.app.plugin.core.datamgr.DataTypesProvider; @@ -93,11 +94,11 @@ public class ShowInstructionInfoPluginScreenShots extends GhidraScreenShotGenera Rectangle shapeBounds = new Rectangle(location, labelSize); int thickness = 5; - drawRectangle(Color.RED, shapeBounds, thickness); + drawRectangle(Palette.RED, shapeBounds, thickness); Point start = getEndOfRow(cb, listingPanel, window); Point end = new Point(location.x, location.y); - drawLine(Color.RED, thickness, start, end); + drawLine(Palette.RED, thickness, start, end); cropListingWithStatusArea(); diff --git a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/TextFormatter.java b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/TextFormatter.java index 2faf062de3..8a74630ae3 100644 --- a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/TextFormatter.java +++ b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/TextFormatter.java @@ -20,6 +20,9 @@ import java.awt.image.BufferedImage; import java.util.regex.Matcher; import java.util.regex.Pattern; +import generic.theme.GThemeDefaults.Colors; +import generic.theme.GThemeDefaults.Colors.Palette; + /** * Each line is sized based on the font height and line padding on the top and bottom of the line. * The y coordinate tracks the top of the line, so a lines baseline (position to draw the text) @@ -37,7 +40,7 @@ public class TextFormatter { private int y; private int leftMargin; private int baselineOffset; - private TextFormatterContext defaultContext = new TextFormatterContext(Color.BLACK); + private TextFormatterContext defaultContext = new TextFormatterContext(Colors.FOREGROUND); private int width; @@ -65,9 +68,9 @@ public class TextFormatter { int height = lineCount * (lineHeight) + topMargin * 2; image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); Graphics2D g = image.createGraphics(); - g.setColor(Color.WHITE); + g.setColor(Colors.BACKGROUND); g.fillRect(0, 0, width, height); - g.setColor(Color.black); + g.setColor(Palette.BLACK); g.drawRect(0, 0, width - 1, height - 1); g.dispose(); } diff --git a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/ThemingScreenShots.java b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/ThemingScreenShots.java new file mode 100644 index 0000000000..8900ad526e --- /dev/null +++ b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/ThemingScreenShots.java @@ -0,0 +1,68 @@ +/* ### + * 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 help.screenshot; + +import java.awt.Font; + +import org.junit.Test; + +import docking.theme.gui.*; +import generic.theme.*; +import generic.theme.GThemeDefaults.Colors.Palette; +import resources.ResourceManager; + +public class ThemingScreenShots extends GhidraScreenShotGenerator { + + public ThemingScreenShots() { + super(); + } + + @Test + public void testThemeDialog() { + showDialogWithoutBlocking(tool, new ThemeDialog()); + captureDialog(1000, 500); + } + + @Test + public void testColorEditor() { + ColorValueEditor editor = new ColorValueEditor(e -> { + /**/}); + ColorValue value = new ColorValue("color.bg.test", Palette.BLUE); + Gui.setColor(value); + editor.editValue(value); + captureDialog(); + } + + @Test + public void testFontEditor() { + FontValueEditor editor = new FontValueEditor(e -> { + /**/}); + FontValue value = new FontValue("font.xyz", new Font("Monospaced", Font.BOLD, 14)); + Gui.setFont(value); + editor.editValue(value); + captureDialog(); + } + + @Test + public void testIconEditor() { + IconValueEditor editor = new IconValueEditor(e -> { + /**/}); + IconValue value = new IconValue("icon.bomb", ResourceManager.getDefaultIcon()); + Gui.setIcon(value); + editor.editValue(value); + captureDialog(); + } +} diff --git a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/ToolScreenShots.java b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/ToolScreenShots.java index 5d7ae7c6d0..7f82a95cb2 100644 --- a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/ToolScreenShots.java +++ b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/ToolScreenShots.java @@ -36,6 +36,9 @@ import docking.tool.ToolConstants; import docking.widgets.OptionDialog; import docking.widgets.table.GTable; import generic.jar.ResourceFile; +import generic.theme.GThemeDefaults.Colors; +import generic.theme.GThemeDefaults.Colors.Java; +import generic.theme.GThemeDefaults.Colors.Palette; import ghidra.framework.Application; import ghidra.framework.LoggingInitialization; import ghidra.framework.cmd.BackgroundCommand; @@ -135,11 +138,12 @@ public class ToolScreenShots extends GhidraScreenShotGenerator { tool.executeBackgroundCommand(new DummyBackgroundCommand(), program); Border inner = BorderFactory.createRaisedBevelBorder(); - Border outer = BorderFactory.createLineBorder(Color.BLACK); + Border outer = BorderFactory.createLineBorder(Java.BORDER); statusBar.setBorder(BorderFactory.createCompoundBorder(outer, inner)); captureComponent(statusBar); program.endTransaction(id, false); - padImage(Color.WHITE, topBottomMargin, leftRightMargin, leftRightMargin, topBottomMargin); + padImage(Colors.FOREGROUND, topBottomMargin, leftRightMargin, leftRightMargin, + topBottomMargin); JComponent statusLabel = (JComponent) getInstanceField("statusLabel", statusBar); Font font = new Font("Ariel", Font.PLAIN, 12); @@ -184,8 +188,9 @@ public class ToolScreenShots extends GhidraScreenShotGenerator { int arrowX = bounds.x + bounds.width / 2 + leftMargin; int arrowStartY = textY - metrics.getHeight() - 5; int arrowEndY = height - topMargin; - drawText(label, Color.BLACK, new Point(textX, textY), font); - drawArrow(Color.BLACK, 1, new Point(arrowX, arrowStartY), new Point(arrowX, arrowEndY), 9); + drawText(label, Colors.FOREGROUND, new Point(textX, textY), font); + drawArrow(Palette.BLACK, 1, new Point(arrowX, arrowStartY), new Point(arrowX, arrowEndY), + 9); } private void labelTop(String label, Rectangle bounds, Font font, FontMetrics metrics, @@ -195,8 +200,9 @@ public class ToolScreenShots extends GhidraScreenShotGenerator { int arrowX = bounds.x + bounds.width / 2 + leftMargin; int arrowStartY = textY + 5; int arrowEndY = topMargin; - drawText(label, Color.BLACK, new Point(textX, textY), font); - drawArrow(Color.BLACK, 1, new Point(arrowX, arrowStartY), new Point(arrowX, arrowEndY), 9); + drawText(label, Colors.FOREGROUND, new Point(textX, textY), font); + drawArrow(Palette.BLACK, 1, new Point(arrowX, arrowStartY), new Point(arrowX, arrowEndY), + 9); } private int getTextStart(Rectangle bounds, FontMetrics metrics, String string) { @@ -261,7 +267,7 @@ public class ToolScreenShots extends GhidraScreenShotGenerator { captureDialog(); JButton button = findButtonByText(window, "Restore Defaults"); - drawRectangleWithDropShadowAround(button, Color.GREEN, 2); + drawRectangleWithDropShadowAround(button, Palette.GREEN, 2); crop(new Rectangle(0, 200, 700, 150)); } diff --git a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/TreesScreenShots.java b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/TreesScreenShots.java index fa7898a400..951b02b580 100644 --- a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/TreesScreenShots.java +++ b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/TreesScreenShots.java @@ -15,7 +15,8 @@ */ package help.screenshot; -import java.awt.*; +import java.awt.Component; +import java.awt.Rectangle; import java.util.Arrays; import javax.swing.*; @@ -38,6 +39,7 @@ import docking.widgets.table.constrainteditor.ColumnConstraintEditor; import docking.widgets.table.constrainteditor.IntegerConstraintEditor; import docking.widgets.table.threaded.ThreadedTableModel; import docking.widgets.tree.*; +import generic.theme.GThemeDefaults.Colors.Palette; import ghidra.app.plugin.core.datamgr.DataTypesProvider; import ghidra.app.plugin.core.datamgr.tree.DataTypeArchiveGTree; import ghidra.app.plugin.core.functionwindow.FunctionWindowProvider; @@ -182,7 +184,7 @@ public class TreesScreenShots extends GhidraScreenShotGenerator { int x = rectangle.x + rectangle.width - (width - padding) - iconPadding; int y = rectangle.y - padding; Rectangle shapeBounds = new Rectangle(x, y, width, height); - drawOval(Color.GREEN.darker(), shapeBounds, thickness); + drawOval(Palette.GREEN, shapeBounds, thickness); } diff --git a/gradle/helpProject.gradle b/gradle/helpProject.gradle index 7f1794b400..a75989f6d7 100644 --- a/gradle/helpProject.gradle +++ b/gradle/helpProject.gradle @@ -132,6 +132,8 @@ task buildHelp(type: JavaExec, dependsOn: indexHelp) { group rootProject.GHIDRA_GROUP description " Builds the help for this module. [gradle/helpProject.gradle]\n" + outputs.upToDateWhen {false} + File helpRootDir = file('src/main/help/help') File outputDir = file('build/help/main/help')