diff --git a/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/gui/tracermi/launcher/AbstractTraceRmiLaunchOpinion.java b/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/gui/tracermi/launcher/AbstractTraceRmiLaunchOpinion.java index def81a2db9..e889ca5348 100644 --- a/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/gui/tracermi/launcher/AbstractTraceRmiLaunchOpinion.java +++ b/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/gui/tracermi/launcher/AbstractTraceRmiLaunchOpinion.java @@ -34,7 +34,8 @@ public abstract class AbstractTraceRmiLaunchOpinion implements TraceRmiLaunchOpi String pluginName = PluginUtils.getPluginNameFromClass(TraceRmiLauncherServicePlugin.class); options.registerOption(TraceRmiLauncherServicePlugin.OPTION_NAME_SCRIPT_PATHS, OptionType.STRING_TYPE, "", new HelpLocation(pluginName, "options"), - "Paths to search for user-created debugger launchers", new ScriptPathsPropertyEditor()); + "Paths to search for user-created debugger launchers", + () -> new ScriptPathsPropertyEditor()); } @Override diff --git a/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/framework/options/AutoOptions.java b/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/framework/options/AutoOptions.java index 498151adfe..20569ee7c8 100644 --- a/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/framework/options/AutoOptions.java +++ b/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/framework/options/AutoOptions.java @@ -27,6 +27,7 @@ import ghidra.framework.options.annotation.*; import ghidra.framework.plugintool.Plugin; import ghidra.framework.plugintool.PluginTool; import ghidra.util.HelpLocation; +import ghidra.util.SystemUtilities; public interface AutoOptions { @@ -148,7 +149,7 @@ public interface AutoOptions { String description = annotation.description(); Class editorClass = annotation.editor(); final PropertyEditor editor; - if (editorClass == PropertyEditor.class) { + if (editorClass == PropertyEditor.class || SystemUtilities.isInHeadlessMode()) { editor = null; } else { @@ -169,16 +170,16 @@ public interface AutoOptions { else if ( is font option ) { // Note: there is no font value to check against for fonts in the new Theme system. - // If annotation fonts are needed, then they should be bound by String id. Likely, - // annotation fonts are not needed now that have themes. We also probably no - // longer need annotation colors either. + // If annotation fonts are needed, then they should be bound by String id. Likely, + // annotation fonts are not needed now that have themes. We also probably no + // longer need annotation colors either. options.registerThemeFontBinding(description, fontId, help, description); } */ else { options.registerOption(key.name, type, defaultValue, help, description, - editor); + () -> editor); // TODO: Wish Ghidra would do this upon any option registration options.putObject(key.name, defaultValue, type); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/ApplyDataArchiveAnalyzer.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/ApplyDataArchiveAnalyzer.java index d2e13ec137..72acebfe12 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/ApplyDataArchiveAnalyzer.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/ApplyDataArchiveAnalyzer.java @@ -126,13 +126,13 @@ public class ApplyDataArchiveAnalyzer extends AbstractAnalyzer { options.registerOption(OPTION_NAME_ARCHIVE_CHOOSER, OptionType.STRING_TYPE, CHOOSER_AUTO_DETECT, null, OPTION_DESCRIPTION_ARCHIVE_CHOOSER, - new StringWithChoicesEditor(chooserList)); + () -> new StringWithChoicesEditor(chooserList)); options.registerOption(OPTION_NAME_GDT_FILEPATH, OptionType.FILE_TYPE, null, null, OPTION_DESCRIPTION_GDT_FILEPATH, - new FileChooserEditor(FileDataTypeManager.GDT_FILEFILTER)); + () -> new FileChooserEditor(FileDataTypeManager.GDT_FILEFILTER)); options.registerOption(OPTION_NAME_PROJECT_PATH, OptionType.STRING_TYPE, null, null, - OPTION_DESCRIPTION_PROJECT_PATH, new ProjectPathChooserEditor( + OPTION_DESCRIPTION_PROJECT_PATH, () -> new ProjectPathChooserEditor( "Choose Data Type Archive", DATATYPEARCHIVE_PROJECT_FILTER)); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/AutoAnalysisPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/AutoAnalysisPlugin.java index eb6d4a01fa..14b2e6b7a3 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/AutoAnalysisPlugin.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/AutoAnalysisPlugin.java @@ -256,7 +256,7 @@ public class AutoAnalysisPlugin extends Plugin implements AutoAnalysisManagerLis analysisMgr.addListener(this); Options options = program.getOptions(Program.ANALYSIS_PROPERTIES); - options.registerOptionsEditor(new AnalysisOptionsEditor(program)); + options.registerOptionsEditor(() -> new AnalysisOptionsEditor(program)); options.setOptionsHelpLocation( new HelpLocation("AutoAnalysisPlugin", "Auto_Analysis_Option")); } @@ -264,7 +264,8 @@ public class AutoAnalysisPlugin extends Plugin implements AutoAnalysisManagerLis private void programActivated(Program program) { program.getOptions(StoredAnalyzerTimes.OPTIONS_LIST) .registerOption(StoredAnalyzerTimes.OPTION_NAME, OptionType.CUSTOM_TYPE, null, null, - "Cumulative analysis task times", new StoredAnalyzerTimesPropertyEditor()); + "Cumulative analysis task times", + () -> new StoredAnalyzerTimesPropertyEditor()); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/rust/RustDemanglerAnalyzer.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/rust/RustDemanglerAnalyzer.java index 3255dd2369..534a781ff4 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/rust/RustDemanglerAnalyzer.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/rust/RustDemanglerAnalyzer.java @@ -15,8 +15,7 @@ */ package ghidra.app.plugin.core.analysis.rust; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; +import java.beans.*; import java.util.Arrays; import docking.options.editor.BooleanEditor; @@ -90,24 +89,14 @@ public class RustDemanglerAnalyzer extends AbstractDemanglerAnalyzer { options.registerOption(OPTION_NAME_DEMANGLE_USE_KNOWN_PATTERNS, demangleOnlyKnownPatterns, help, OPTION_DESCRIPTION_USE_KNOWN_PATTERNS); - BooleanEditor deprecatedEditor = null; - FormatEditor formatEditor = null; - if (!SystemUtilities.isInHeadlessMode()) { - // Only add the custom options editor when not headless. The custom editor allows - // the list of choices presented to the user to change depending on the state of the - // useDeprecatedDemangler flag. - deprecatedEditor = new BooleanEditor(); - deprecatedEditor.setValue(Boolean.valueOf(useDeprecatedDemangler)); - formatEditor = new FormatEditor(demanglerFormat, deprecatedEditor); - deprecatedEditor.addPropertyChangeListener(formatEditor); - } + RustOptionsEditor optionsEditor = new RustOptionsEditor(); options.registerOption(OPTION_NAME_USE_DEPRECATED_DEMANGLER, OptionType.BOOLEAN_TYPE, useDeprecatedDemangler, help, OPTION_DESCRIPTION_DEPRECATED_DEMANGLER, - deprecatedEditor); + () -> optionsEditor.getDeprecatedNameEditor()); options.registerOption(OPTION_NAME_DEMANGLER_FORMAT, OptionType.ENUM_TYPE, demanglerFormat, - help, OPTION_DESCRIPTION_DEMANGLER_FORMAT, formatEditor); + help, OPTION_DESCRIPTION_DEMANGLER_FORMAT, () -> optionsEditor.getFormatEditor()); } @Override @@ -162,6 +151,45 @@ public class RustDemanglerAnalyzer extends AbstractDemanglerAnalyzer { super.apply(program, address, demangledVariable, options, log, monitor); } +//================================================================================================== +// Inner Classes +//================================================================================================== + + // We only use the editor when not headless, since GUI code in headless will throw an exception. + // Further, the options below have a relationship, so we need to build them together. + // The format editor's list of choices presented to the user will change depending on the state + // of the deprecated boolean editor. + private class RustOptionsEditor { + + private BooleanEditor deprecatedEditor; + private FormatEditor formatEditor; + + private void lazyInit() { + if (SystemUtilities.isInHeadlessMode()) { + return; // the editor should not be requested in headless mode + } + + if (deprecatedEditor != null) { + return; // already loaded + } + + deprecatedEditor = new BooleanEditor(); + deprecatedEditor.setValue(Boolean.valueOf(useDeprecatedDemangler)); + formatEditor = new FormatEditor(demanglerFormat, deprecatedEditor); + deprecatedEditor.addPropertyChangeListener(formatEditor); + } + + PropertyEditor getDeprecatedNameEditor() { + lazyInit(); + return deprecatedEditor; + } + + PropertyEditor getFormatEditor() { + lazyInit(); + return formatEditor; + } + } + private static class FormatEditor extends EnumEditor implements PropertyChangeListener { private final FormatSelector selector; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/blockmodel/BlockModelServicePlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/blockmodel/BlockModelServicePlugin.java index bbf155d607..4293db67b9 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/blockmodel/BlockModelServicePlugin.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/blockmodel/BlockModelServicePlugin.java @@ -37,11 +37,11 @@ import ghidra.util.datastruct.WeakDataStructureFactory; import ghidra.util.datastruct.WeakSet; import ghidra.util.exception.NotFoundException; -/** +/** * Provides a service for tracking the selected basic/subroutine block models for a tool. * Methods are provided for obtaining an instance of the active or arbitrary block model. - * A new model instance is always provided since the internal cache will quickly become - * stale based upon program changes. The current model implementations do not handle + * A new model instance is always provided since the internal cache will quickly become + * stale based upon program changes. The current model implementations do not handle * program changes which would invalidate the cached blocks stored within the model. * * A single basic/sub model list is maintained since it is possible that some uses @@ -111,9 +111,12 @@ public class BlockModelServicePlugin extends ProgramPlugin // Install model selection option in Tool panel options = tool.getOptions(ToolConstants.TOOL_OPTIONS); - editor = new StringWithChoicesEditor(availableModelNames); options.registerOption(SUB_OPTION, OptionType.STRING_TYPE, selectedSubroutineModelName, - null, "The default subroutine model used when creating call graphs.", editor); + null, "The default subroutine model used when creating call graphs.", + () -> { + editor = new StringWithChoicesEditor(availableModelNames); + return editor; + }); setPreferedModel(options); updateModelOptions(); options.addOptionsChangeListener(this); @@ -168,7 +171,9 @@ public class BlockModelServicePlugin extends ProgramPlugin modelUpdateInProgress = true; try { - editor.setChoices(availableModelNames); + if (editor != null) { + editor.setChoices(availableModelNames); + } options.setString(SUB_OPTION, selectedSubroutineModelName); } finally { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/AbstractCodeBrowserPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/AbstractCodeBrowserPlugin.java index 38945157ff..d8fd98c53e 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/AbstractCodeBrowserPlugin.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/AbstractCodeBrowserPlugin.java @@ -98,7 +98,7 @@ public abstract class AbstractCodeBrowserPlugin

ex ToolOptions displayOptions = tool.getOptions(GhidraOptions.CATEGORY_BROWSER_DISPLAY); ToolOptions fieldOptions = tool.getOptions(GhidraOptions.CATEGORY_BROWSER_FIELDS); - displayOptions.registerOptionsEditor(new ListingDisplayOptionsEditor(displayOptions)); + displayOptions.registerOptionsEditor(() -> new ListingDisplayOptionsEditor(displayOptions)); displayOptions.setOptionsHelpLocation( new HelpLocation(getName(), GhidraOptions.CATEGORY_BROWSER_DISPLAY)); fieldOptions.setOptionsHelpLocation( @@ -616,7 +616,7 @@ public abstract class AbstractCodeBrowserPlugin

ex options.registerOption(ManualViewerCommandWrappedOption.MANUAL_VIEWER_OPTIONS, OptionType.CUSTOM_TYPE, ManualViewerCommandWrappedOption.getDefaultBrowserLoaderOptions(), helpLocation, - "Options for running manual viewer", new ManualViewerCommandEditor()); + "Options for running manual viewer", () -> new ManualViewerCommandEditor()); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/progmgr/ProgramManagerPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/progmgr/ProgramManagerPlugin.java index a34139999a..8605ac8e9b 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/progmgr/ProgramManagerPlugin.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/progmgr/ProgramManagerPlugin.java @@ -606,7 +606,7 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager, Opti PropertyEditor editor = options.getPropertyEditor(filePropertyName); if (editor == null && options.getType(filePropertyName) == OptionType.STRING_TYPE) { options.registerOption(filePropertyName, OptionType.STRING_TYPE, null, null, null, - new StringBasedFileEditor()); + () -> new StringBasedFileEditor()); } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/AddressFieldFactory.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/AddressFieldFactory.java index 89b1d31480..9d29e56038 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/AddressFieldFactory.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/AddressFieldFactory.java @@ -15,7 +15,6 @@ */ package ghidra.app.util.viewer.field; -import java.beans.PropertyEditor; import java.math.BigInteger; import docking.widgets.fieldpanel.field.*; @@ -47,7 +46,6 @@ public class AddressFieldFactory extends FieldFactory { private boolean padZeros; private int minHexDigits; private boolean rightJustify; - private PropertyEditor addressFieldOptionsEditor = new AddressFieldOptionsPropertyEditor(); /** * Default Constructor @@ -74,7 +72,7 @@ public class AddressFieldFactory extends FieldFactory { fieldOptions.registerOption(ADDRESS_DISPLAY_OPTIONS_NAME, OptionType.CUSTOM_TYPE, new AddressFieldOptionsWrappedOption(), helpLoc, "Adjusts the Address Field display", - addressFieldOptionsEditor); + () -> new AddressFieldOptionsPropertyEditor()); CustomOption customOption = fieldOptions.getCustomOption(ADDRESS_DISPLAY_OPTIONS_NAME, null); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/ArrayValuesFieldFactory.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/ArrayValuesFieldFactory.java index 3f83282ad9..a467b26514 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/ArrayValuesFieldFactory.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/ArrayValuesFieldFactory.java @@ -15,7 +15,6 @@ */ package ghidra.app.util.viewer.field; -import java.beans.PropertyEditor; import java.math.BigInteger; import docking.widgets.fieldpanel.field.*; @@ -35,7 +34,6 @@ import ghidra.util.exception.AssertException; public class ArrayValuesFieldFactory extends FieldFactory { public static final String FIELD_NAME = "Array Values"; private int valuesPerLine; - private PropertyEditor arrayOptionsEditor = new ArrayElementPropertyEditor(); public ArrayValuesFieldFactory() { super(FIELD_NAME); @@ -66,7 +64,7 @@ public class ArrayValuesFieldFactory extends FieldFactory { // we need to install a custom editor that allows us to edit a group of related options fieldOptions.registerOption(FormatManager.ARRAY_DISPLAY_OPTIONS, OptionType.CUSTOM_TYPE, new ArrayElementWrappedOption(), null, FormatManager.ARRAY_DISPLAY_DESCRIPTION, - arrayOptionsEditor); + () -> new ArrayElementPropertyEditor()); CustomOption wrappedOption = fieldOptions.getCustomOption( FormatManager.ARRAY_DISPLAY_OPTIONS, new ArrayElementWrappedOption()); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/BrowserCodeUnitFormatOptions.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/BrowserCodeUnitFormatOptions.java index 9b0993ece7..83e302d438 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/BrowserCodeUnitFormatOptions.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/BrowserCodeUnitFormatOptions.java @@ -58,8 +58,8 @@ public class BrowserCodeUnitFormatOptions extends CodeUnitFormatOptions GhidraOptions.OPERAND_GROUP_TITLE + Options.DELIMITER + "Always Show Primary Reference"; /** - * Option for whether to follow referenced pointers, for read or indirect reference types, - * to show pointer's referenced symbol instead of symbol at pointer. When applied the + * Option for whether to follow referenced pointers, for read or indirect reference types, + * to show pointer's referenced symbol instead of symbol at pointer. When applied the * resulting label will be preceded by ->. */ private final static String FOLLOW_POINTER_REFERENCE_MARKUP_OPTION = @@ -98,7 +98,7 @@ public class BrowserCodeUnitFormatOptions extends CodeUnitFormatOptions * This constructor must be used by the field factory since an OptionsService may * not obtainable at the time they are constructed. * @param fieldOptions field options - * @param autoUpdate if true format will auto update if associated options are changed, in + * @param autoUpdate if true format will auto update if associated options are changed, in * addition any listeners will be notified when this format is updated. */ BrowserCodeUnitFormatOptions(ToolOptions fieldOptions, boolean autoUpdate) { @@ -110,7 +110,7 @@ public class BrowserCodeUnitFormatOptions extends CodeUnitFormatOptions if (!exists) { fieldOptions.registerOption(NAMESPACE_OPTIONS, OptionType.CUSTOM_TYPE, new NamespaceWrappedOption(), null, NAMESPACE_OPTIONS_DESCRIPTIONS, - new NamespacePropertyEditor()); + () -> new NamespacePropertyEditor()); HelpLocation hl = new HelpLocation("CodeBrowserPlugin", "Operands_Field"); fieldOptions.getOptions(GhidraOptions.OPERAND_GROUP_TITLE).setOptionsHelpLocation(hl); @@ -165,7 +165,7 @@ public class BrowserCodeUnitFormatOptions extends CodeUnitFormatOptions private void updateFormat() { fieldOptions.registerOption(NAMESPACE_OPTIONS, OptionType.CUSTOM_TYPE, new NamespaceWrappedOption(), null, NAMESPACE_OPTIONS_DESCRIPTIONS, - new NamespacePropertyEditor()); + () -> new NamespacePropertyEditor()); CustomOption customOption = fieldOptions.getCustomOption(NAMESPACE_OPTIONS, new NamespaceWrappedOption()); if (!(customOption instanceof NamespaceWrappedOption)) { @@ -240,9 +240,9 @@ public class BrowserCodeUnitFormatOptions extends CodeUnitFormatOptions } /** - * Get current state of the Follow Referenced Pointers option. - * @return true if operand pointer read of indirect references will be followed and - * non-dynamic pointer referenced symbol will be rendered in place of pointer label. + * Get current state of the Follow Referenced Pointers option. + * @return true if operand pointer read of indirect references will be followed and + * non-dynamic pointer referenced symbol will be rendered in place of pointer label. */ public boolean followReferencedPointers() { return followReferencedPointers; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/EolCommentFieldFactory.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/EolCommentFieldFactory.java index b457ea1841..884f8a6c45 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/EolCommentFieldFactory.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/EolCommentFieldFactory.java @@ -16,7 +16,6 @@ package ghidra.app.util.viewer.field; import java.awt.Color; -import java.beans.PropertyEditor; import java.math.BigInteger; import java.util.ArrayList; import java.util.List; @@ -67,7 +66,6 @@ public class EolCommentFieldFactory extends FieldFactory { private int refRepeatableCommentStyle; private EolExtraCommentsOption extraCommentsOption = new EolExtraCommentsOption(); - private PropertyEditor extraCommmentsEditor = new EolExtraCommentsPropertyEditor(); // The codeUnitFormatOptions is used to monitor "follow pointer..." option to avoid duplication // of data within auto-comment. We don't bother adding a listener to kick the model since this @@ -126,7 +124,7 @@ public class EolCommentFieldFactory extends FieldFactory { private void setupAutoCommentOptions(Options fieldOptions, HelpLocation hl) { fieldOptions.registerOption(EXTRA_COMMENT_KEY, OptionType.CUSTOM_TYPE, new EolExtraCommentsOption(), hl, "The group of auto comment options", - extraCommmentsEditor); + () -> new EolExtraCommentsPropertyEditor()); CustomOption customOption = fieldOptions.getCustomOption(EXTRA_COMMENT_KEY, null); if (!(customOption instanceof EolExtraCommentsOption)) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/FileOffsetFieldFactory.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/FileOffsetFieldFactory.java index 07a2c376dd..5a7280f49b 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/FileOffsetFieldFactory.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/FileOffsetFieldFactory.java @@ -15,7 +15,6 @@ */ package ghidra.app.util.viewer.field; -import java.beans.PropertyEditor; import java.math.BigInteger; import docking.widgets.fieldpanel.field.*; @@ -49,8 +48,6 @@ public class FileOffsetFieldFactory extends FieldFactory { private boolean showFilename; private boolean useHex; - private PropertyEditor fileOffsetFieldOptionsEditor = - new FileOffsetFieldOptionsPropertyEditor(); /** * Default Constructor @@ -77,7 +74,8 @@ public class FileOffsetFieldFactory extends FieldFactory { fieldOptions.registerOption(FILE_OFFSET_DISPLAY_OPTIONS_NAME, OptionType.CUSTOM_TYPE, new FileOffsetFieldOptionsWrappedOption(), helpLoc, - "Adjusts the File Offset Field display", fileOffsetFieldOptionsEditor); + "Adjusts the File Offset Field display", + () -> new FileOffsetFieldOptionsPropertyEditor()); CustomOption customOption = fieldOptions.getCustomOption(FILE_OFFSET_DISPLAY_OPTIONS_NAME, null); @@ -97,7 +95,8 @@ public class FileOffsetFieldFactory extends FieldFactory { @Override public FieldFactory newInstance(FieldFormatModel formatModel, - ListingHighlightProvider highlightProvider, ToolOptions options, ToolOptions fieldOptions) { + ListingHighlightProvider highlightProvider, ToolOptions options, + ToolOptions fieldOptions) { return new FileOffsetFieldFactory(formatModel, highlightProvider, options, fieldOptions); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/LabelFieldFactory.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/LabelFieldFactory.java index 03a0155cd1..cd5d7b0ee3 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/LabelFieldFactory.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/LabelFieldFactory.java @@ -15,7 +15,6 @@ */ package ghidra.app.util.viewer.field; -import java.beans.PropertyEditor; import java.math.BigInteger; import java.util.*; @@ -60,8 +59,6 @@ public class LabelFieldFactory extends FieldFactory { private Icon ANCHOR_ICON = new MultiIcon(EMPTY_ICON, new GIcon("icon.base.util.viewer.fieldfactory.label")); - private PropertyEditor namespaceOptionsEditor = new NamespacePropertyEditor(); - private boolean displayFunctionLabel; private boolean displayLocalNamespace; private boolean displayNonLocalNamespace; @@ -107,7 +104,7 @@ public class LabelFieldFactory extends FieldFactory { // we need to install a custom editor that allows us to edit a group of related options fieldOptions.registerOption(NAMESPACE_OPTIONS, OptionType.CUSTOM_TYPE, new NamespaceWrappedOption(), null, "Adjusts the Label Field namespace display", - namespaceOptionsEditor); + () -> new NamespacePropertyEditor()); CustomOption wrappedOption = fieldOptions.getCustomOption(NAMESPACE_OPTIONS, new NamespaceWrappedOption()); if (!(wrappedOption instanceof NamespaceWrappedOption)) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/XRefFieldFactory.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/XRefFieldFactory.java index a7b127977c..06260f24ad 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/XRefFieldFactory.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/XRefFieldFactory.java @@ -17,7 +17,6 @@ package ghidra.app.util.viewer.field; import java.awt.Color; import java.awt.FontMetrics; -import java.beans.PropertyEditor; import java.math.BigInteger; import java.util.*; import java.util.Map.Entry; @@ -75,8 +74,6 @@ public class XRefFieldFactory extends FieldFactory { static final String GROUP_BY_FUNCTION_KEY = GROUP_TITLE + Options.DELIMITER + "Group by Function"; - private PropertyEditor namespaceOptionsEditor = new NamespacePropertyEditor(); - protected String delim = DELIMITER; protected boolean displayBlockName; protected boolean groupByFunction; @@ -168,7 +165,7 @@ public class XRefFieldFactory extends FieldFactory { // we need to install a custom editor that allows us to edit a group of related options fieldOptions.registerOption(NAMESPACE_OPTIONS_KEY, OptionType.CUSTOM_TYPE, new NamespaceWrappedOption(), new HelpLocation("CodeBrowserPlugin", "XREFs_Field"), - "Adjusts the XREFs Field namespace display", namespaceOptionsEditor); + "Adjusts the XREFs Field namespace display", () -> new NamespacePropertyEditor()); CustomOption customOption = fieldOptions.getCustomOption(NAMESPACE_OPTIONS_KEY, null); if (!(customOption instanceof NamespaceWrappedOption)) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/format/FormatManager.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/format/FormatManager.java index 33c8111386..ff085e412a 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/format/FormatManager.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/format/FormatManager.java @@ -71,7 +71,7 @@ public class FormatManager implements OptionsChangeListener { TemplateSimplifier templateSimplifier; // NOTE: Unused custom format code was removed. The custom format code last existed in - // commit #204e7892bf2f110ebb05ca4beee3fe5b397f88c9. + // commit #204e7892bf2f110ebb05ca4beee3fe5b397f88c9. /** * Constructs a new FormatManager. @@ -100,7 +100,7 @@ public class FormatManager implements OptionsChangeListener { private void getArrayDisplayOptions(Options options) { options.registerOption(ARRAY_DISPLAY_OPTIONS, OptionType.CUSTOM_TYPE, new ArrayElementWrappedOption(), null, ARRAY_DISPLAY_DESCRIPTION, - new ArrayElementPropertyEditor()); + () -> new ArrayElementPropertyEditor()); CustomOption option = options.getCustomOption(ARRAY_DISPLAY_OPTIONS, null); if (option instanceof ArrayElementWrappedOption) { ArrayElementWrappedOption arrayOption = (ArrayElementWrappedOption) option; @@ -174,7 +174,7 @@ public class FormatManager implements OptionsChangeListener { /** * Returns the total number of model in the format manager. - * @return the total number of model in the format manager + * @return the total number of model in the format manager */ public int getNumModels() { return NUM_MODELS; @@ -200,7 +200,7 @@ public class FormatManager implements OptionsChangeListener { /** * Returns the format model for the plate field. - * @return the format model for the plate field + * @return the format model for the plate field */ public FieldFormatModel getPlateFormat() { return models[FieldFormatModel.PLATE]; @@ -279,7 +279,7 @@ public class FormatManager implements OptionsChangeListener { /** * Returns the Options used for field specific properties. - * @return the Options used for field specific properties + * @return the Options used for field specific properties */ public ToolOptions getFieldOptions() { return fieldOptions; @@ -847,7 +847,7 @@ public class FormatManager implements OptionsChangeListener { } /** - * Gets all {@link ListingHighlightProvider}s installed on this FormatManager via the + * Gets all {@link ListingHighlightProvider}s installed on this FormatManager via the * {@link #addHighlightProvider(ListingHighlightProvider)}. * * @return all {@link ListingHighlightProvider}s installed on this FormatManager. @@ -930,12 +930,12 @@ public class FormatManager implements OptionsChangeListener { public Highlight[] createHighlights(String text, ListingField field, int cursorTextOffset) { // - // Gather and use all other registered providers. - // + // Gather and use all other registered providers. + // // Note: we loop backwards here as a hacky method to make sure that the middle-mouse - // highlighter runs last and is thus painted above other highlights. This - // works because the middle-mouse highlighter is installed before any other - // highlighters. + // highlighter runs last and is thus painted above other highlights. This + // works because the middle-mouse highlighter is installed before any other + // highlighters. List list = new ArrayList<>(); int size = highlightProviders.size(); for (int i = size - 1; i >= 0; i--) { diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/misc/PropertyEditorTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/misc/PropertyEditorTest.java index 04709a96a7..8722978282 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/misc/PropertyEditorTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/misc/PropertyEditorTest.java @@ -15,8 +15,7 @@ */ package ghidra.app.plugin.core.misc; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.*; import java.awt.Component; import java.awt.Container; @@ -53,7 +52,7 @@ public class PropertyEditorTest extends AbstractGhidraHeadedIntegrationTest { runSwing(() -> DockingDialog.createDialog(null, dialogComponent, null)); SwingUtilities.invokeLater(() -> editorDialog.setVisible(true)); - waitForJDialog(null, "Test Properties", 500); + waitForJDialog("Test Properties"); assertNotNull("Dialog failed to launch", editorDialog); waitForSwing(); waitForOptionsTree(dialogComponent); @@ -219,7 +218,7 @@ public class PropertyEditorTest extends AbstractGhidraHeadedIntegrationTest { Options options = new ToolOptions("Test"); String[] choices = new String[] { "abc", "def", "ghi", "jkl" }; options.registerOption("TestStringWithChoices", OptionType.STRING_TYPE, choices[0], null, - "String Choices", new StringWithChoicesEditor(choices)); + "String Choices", () -> new StringWithChoicesEditor(choices)); dialog = showEditor(options); diff --git a/Ghidra/Features/Base/src/test/java/ghidra/framework/data/OptionsDBTest.java b/Ghidra/Features/Base/src/test/java/ghidra/framework/data/OptionsDBTest.java index 9df103ea09..62bb3efeee 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/framework/data/OptionsDBTest.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/framework/data/OptionsDBTest.java @@ -303,7 +303,7 @@ public class OptionsDBTest extends AbstractDockingTest { @Test public void testRegisterPropertyEditor() { MyPropertyEditor editor = new MyPropertyEditor(); - options.registerOption("foo", OptionType.INT_TYPE, 5, null, "description", editor); + options.registerOption("foo", OptionType.INT_TYPE, 5, null, "description", () -> editor); assertEquals(editor, options.getRegisteredPropertyEditor("foo")); } @@ -561,12 +561,11 @@ public class OptionsDBTest extends AbstractDockingTest { @Test public void testRegisteringOptionsEditor() { MyOptionsEditor myOptionsEditor = new MyOptionsEditor(); - options.registerOptionsEditor(myOptionsEditor); + options.registerOptionsEditor(() -> myOptionsEditor); assertEquals(myOptionsEditor, options.getOptionsEditor()); Options subOptions = options.getOptions("SUB"); - subOptions.registerOptionsEditor(myOptionsEditor); + subOptions.registerOptionsEditor(() -> myOptionsEditor); assertEquals(myOptionsEditor, subOptions.getOptionsEditor()); - } @Test diff --git a/Ghidra/Features/Base/src/test/java/ghidra/framework/options/OptionsTest.java b/Ghidra/Features/Base/src/test/java/ghidra/framework/options/OptionsTest.java index 3b627130c3..68e970cc52 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/framework/options/OptionsTest.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/framework/options/OptionsTest.java @@ -296,7 +296,8 @@ public class OptionsTest extends AbstractGuiTest { @Test public void testRegisterPropertyEditor() { MyPropertyEditor editor = new MyPropertyEditor(); - options.registerOption("color", OptionType.COLOR_TYPE, Color.RED, null, "foo", editor); + options.registerOption("color", OptionType.COLOR_TYPE, Color.RED, null, "foo", + () -> editor); assertEquals(editor, options.getRegisteredPropertyEditor("color")); } @@ -570,10 +571,10 @@ public class OptionsTest extends AbstractGuiTest { @Test public void testRegisteringOptionsEditor() { MyOptionsEditor myOptionsEditor = new MyOptionsEditor(); - options.registerOptionsEditor(myOptionsEditor); + options.registerOptionsEditor(() -> myOptionsEditor); assertEquals(myOptionsEditor, options.getOptionsEditor()); Options subOptions = options.getOptions("SUB"); - subOptions.registerOptionsEditor(myOptionsEditor); + subOptions.registerOptionsEditor(() -> myOptionsEditor); assertEquals(myOptionsEditor, subOptions.getOptionsEditor()); } diff --git a/Ghidra/Features/GnuDemangler/src/main/java/ghidra/app/plugin/core/analysis/GnuDemanglerAnalyzer.java b/Ghidra/Features/GnuDemangler/src/main/java/ghidra/app/plugin/core/analysis/GnuDemanglerAnalyzer.java index 52a70190c5..252ed6b38c 100644 --- a/Ghidra/Features/GnuDemangler/src/main/java/ghidra/app/plugin/core/analysis/GnuDemanglerAnalyzer.java +++ b/Ghidra/Features/GnuDemangler/src/main/java/ghidra/app/plugin/core/analysis/GnuDemanglerAnalyzer.java @@ -15,8 +15,7 @@ */ package ghidra.app.plugin.core.analysis; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; +import java.beans.*; import java.util.Arrays; import docking.options.editor.BooleanEditor; @@ -93,24 +92,16 @@ public class GnuDemanglerAnalyzer extends AbstractDemanglerAnalyzer { options.registerOption(OPTION_NAME_DEMANGLE_USE_KNOWN_PATTERNS, demangleOnlyKnownPatterns, help, OPTION_DESCRIPTION_USE_KNOWN_PATTERNS); - BooleanEditor deprecatedEditor = null; - FormatEditor formatEditor = null; - if (!SystemUtilities.isInHeadlessMode()) { - // Only add the custom options editor when not headless. The custom editor allows - // the list of choices presented to the user to change depending on the state of the - // useDeprecatedDemangler flag. - deprecatedEditor = new BooleanEditor(); - deprecatedEditor.setValue(Boolean.valueOf(useDeprecatedDemangler)); - formatEditor = new FormatEditor(demanglerFormat, deprecatedEditor); - deprecatedEditor.addPropertyChangeListener(formatEditor); - } + GnuOptionsEditor optionsEditor = new GnuOptionsEditor(); options.registerOption(OPTION_NAME_USE_DEPRECATED_DEMANGLER, OptionType.BOOLEAN_TYPE, useDeprecatedDemangler, help, OPTION_DESCRIPTION_DEPRECATED_DEMANGLER, - deprecatedEditor); + () -> optionsEditor.getDeprecatedNameEditor()); options.registerOption(OPTION_NAME_DEMANGLER_FORMAT, OptionType.ENUM_TYPE, - demanglerFormat, help, OPTION_DESCRIPTION_DEMANGLER_FORMAT, formatEditor); + demanglerFormat, help, OPTION_DESCRIPTION_DEMANGLER_FORMAT, + () -> optionsEditor.getFormatEditor()); + } @Override @@ -143,6 +134,45 @@ public class GnuDemanglerAnalyzer extends AbstractDemanglerAnalyzer { return demangler.demangle(mangled, demanglerOtions); } +//================================================================================================== +// Inner Classes +//================================================================================================== + + // We only use the editor when not headless, since GUI code in headless will throw an exception. + // Further, the options below have a relationship, so we need to build them together. + // The format editor's list of choices presented to the user will change depending on the state + // of the deprecated boolean editor. + private class GnuOptionsEditor { + + private BooleanEditor deprecatedEditor; + private FormatEditor formatEditor; + + private void lazyInit() { + if (SystemUtilities.isInHeadlessMode()) { + return; // the editor should not be requested in headless mode + } + + if (deprecatedEditor != null) { + return; // already loaded + } + + deprecatedEditor = new BooleanEditor(); + deprecatedEditor.setValue(Boolean.valueOf(useDeprecatedDemangler)); + formatEditor = new FormatEditor(demanglerFormat, deprecatedEditor); + deprecatedEditor.addPropertyChangeListener(formatEditor); + } + + PropertyEditor getDeprecatedNameEditor() { + lazyInit(); + return deprecatedEditor; + } + + PropertyEditor getFormatEditor() { + lazyInit(); + return formatEditor; + } + } + private static class FormatEditor extends EnumEditor implements PropertyChangeListener { private final FormatSelector selector; @@ -221,6 +251,5 @@ public class GnuDemanglerAnalyzer extends AbstractDemanglerAnalyzer { void setFormat(GnuDemanglerFormat format) { setSelectedItem(format.name()); } - } } diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/plugin/VTPlugin.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/plugin/VTPlugin.java index 9bbd0e9624..c83da509d1 100644 --- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/plugin/VTPlugin.java +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/plugin/VTPlugin.java @@ -133,7 +133,7 @@ public class VTPlugin extends Plugin { private void initializeOptions() { Options options = tool.getOptions(GhidraOptions.CATEGORY_BROWSER_DISPLAY); - options.registerOptionsEditor(new ListingDisplayOptionsEditor(options)); + options.registerOptionsEditor(() -> new ListingDisplayOptionsEditor(options)); options.setOptionsHelpLocation(new HelpLocation(CodeBrowserPlugin.class.getSimpleName(), GhidraOptions.CATEGORY_BROWSER_DISPLAY)); diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/provider/matchtable/VTMatchTableProvider.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/provider/matchtable/VTMatchTableProvider.java index 478b09cfd9..a6110bd493 100644 --- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/provider/matchtable/VTMatchTableProvider.java +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/provider/matchtable/VTMatchTableProvider.java @@ -15,159 +15,34 @@ */ package ghidra.feature.vt.gui.provider.matchtable; -import static ghidra.feature.vt.gui.actions.TableSelectionTrackingState.MAINTAIN_SELECTED_ROW_INDEX; -import static ghidra.feature.vt.gui.actions.TableSelectionTrackingState.MAINTAIN_SELECTED_ROW_VALUE; -import static ghidra.feature.vt.gui.actions.TableSelectionTrackingState.NO_SELECTION_TRACKING; -import static ghidra.feature.vt.gui.plugin.VTPlugin.FILTERED_ICON; -import static ghidra.feature.vt.gui.plugin.VTPlugin.UNFILTERED_ICON; -import static ghidra.feature.vt.gui.util.VTOptionDefines.ACCEPT_MATCH_OPTIONS_NAME; -import static ghidra.feature.vt.gui.util.VTOptionDefines.APPLY_DATA_NAME_ON_ACCEPT; -import static ghidra.feature.vt.gui.util.VTOptionDefines.APPLY_FUNCTION_NAME_ON_ACCEPT; -import static ghidra.feature.vt.gui.util.VTOptionDefines.APPLY_IMPLIED_MATCHES_OPTION; -import static ghidra.feature.vt.gui.util.VTOptionDefines.APPLY_MARKUP_OPTIONS_NAME; -import static ghidra.feature.vt.gui.util.VTOptionDefines.AUTO_CREATE_IMPLIED_MATCH; -import static ghidra.feature.vt.gui.util.VTOptionDefines.AUTO_VT_DATA_CORRELATOR; -import static ghidra.feature.vt.gui.util.VTOptionDefines.AUTO_VT_DUPLICATE_FUNCTION_CORRELATOR; -import static ghidra.feature.vt.gui.util.VTOptionDefines.AUTO_VT_EXACT_FUNCTION_CORRELATORS; -import static ghidra.feature.vt.gui.util.VTOptionDefines.AUTO_VT_IMPLIED_MATCH_CORRELATOR; -import static ghidra.feature.vt.gui.util.VTOptionDefines.AUTO_VT_OPTIONS_NAME; -import static ghidra.feature.vt.gui.util.VTOptionDefines.AUTO_VT_REFERENCE_CORRELATORS; -import static ghidra.feature.vt.gui.util.VTOptionDefines.AUTO_VT_SYMBOL_CORRELATOR; -import static ghidra.feature.vt.gui.util.VTOptionDefines.CALLING_CONVENTION; -import static ghidra.feature.vt.gui.util.VTOptionDefines.CALL_FIXUP; -import static ghidra.feature.vt.gui.util.VTOptionDefines.CREATE_IMPLIED_MATCHES_OPTION; -import static ghidra.feature.vt.gui.util.VTOptionDefines.DATA_CORRELATOR_MIN_LEN_OPTION; -import static ghidra.feature.vt.gui.util.VTOptionDefines.DATA_MATCH_DATA_TYPE; -import static ghidra.feature.vt.gui.util.VTOptionDefines.DEFAULT_OPTION_FOR_CALLING_CONVENTION; -import static ghidra.feature.vt.gui.util.VTOptionDefines.DEFAULT_OPTION_FOR_CALL_FIXUP; -import static ghidra.feature.vt.gui.util.VTOptionDefines.DEFAULT_OPTION_FOR_DATA_MATCH_DATA_TYPE; -import static ghidra.feature.vt.gui.util.VTOptionDefines.DEFAULT_OPTION_FOR_EOL_COMMENTS; -import static ghidra.feature.vt.gui.util.VTOptionDefines.DEFAULT_OPTION_FOR_FUNCTION_NAME; -import static ghidra.feature.vt.gui.util.VTOptionDefines.DEFAULT_OPTION_FOR_FUNCTION_RETURN_TYPE; -import static ghidra.feature.vt.gui.util.VTOptionDefines.DEFAULT_OPTION_FOR_FUNCTION_SIGNATURE; -import static ghidra.feature.vt.gui.util.VTOptionDefines.DEFAULT_OPTION_FOR_HIGHEST_NAME_PRIORITY; -import static ghidra.feature.vt.gui.util.VTOptionDefines.DEFAULT_OPTION_FOR_IGNORE_EXCLUDED_MARKUP_ITEMS; -import static ghidra.feature.vt.gui.util.VTOptionDefines.DEFAULT_OPTION_FOR_IGNORE_INCOMPLETE_MARKUP_ITEMS; -import static ghidra.feature.vt.gui.util.VTOptionDefines.DEFAULT_OPTION_FOR_INLINE; -import static ghidra.feature.vt.gui.util.VTOptionDefines.DEFAULT_OPTION_FOR_LABELS; -import static ghidra.feature.vt.gui.util.VTOptionDefines.DEFAULT_OPTION_FOR_NO_RETURN; -import static ghidra.feature.vt.gui.util.VTOptionDefines.DEFAULT_OPTION_FOR_PARAMETER_COMMENTS; -import static ghidra.feature.vt.gui.util.VTOptionDefines.DEFAULT_OPTION_FOR_PARAMETER_DATA_TYPES; -import static ghidra.feature.vt.gui.util.VTOptionDefines.DEFAULT_OPTION_FOR_PARAMETER_NAMES; -import static ghidra.feature.vt.gui.util.VTOptionDefines.DEFAULT_OPTION_FOR_PARAMETER_NAMES_REPLACE_IF_SAME_PRIORITY; -import static ghidra.feature.vt.gui.util.VTOptionDefines.DEFAULT_OPTION_FOR_PLATE_COMMENTS; -import static ghidra.feature.vt.gui.util.VTOptionDefines.DEFAULT_OPTION_FOR_POST_COMMENTS; -import static ghidra.feature.vt.gui.util.VTOptionDefines.DEFAULT_OPTION_FOR_PRE_COMMENTS; -import static ghidra.feature.vt.gui.util.VTOptionDefines.DEFAULT_OPTION_FOR_REPEATABLE_COMMENTS; -import static ghidra.feature.vt.gui.util.VTOptionDefines.DEFAULT_OPTION_FOR_VAR_ARGS; -import static ghidra.feature.vt.gui.util.VTOptionDefines.DISPLAY_APPLY_MARKUP_OPTIONS; -import static ghidra.feature.vt.gui.util.VTOptionDefines.DUPE_FUNCTION_CORRELATOR_MIN_LEN_OPTION; -import static ghidra.feature.vt.gui.util.VTOptionDefines.END_OF_LINE_COMMENT; -import static ghidra.feature.vt.gui.util.VTOptionDefines.FUNCTION_CORRELATOR_MIN_LEN_OPTION; -import static ghidra.feature.vt.gui.util.VTOptionDefines.FUNCTION_NAME; -import static ghidra.feature.vt.gui.util.VTOptionDefines.FUNCTION_RETURN_TYPE; -import static ghidra.feature.vt.gui.util.VTOptionDefines.FUNCTION_SIGNATURE; -import static ghidra.feature.vt.gui.util.VTOptionDefines.HIGHEST_NAME_PRIORITY; -import static ghidra.feature.vt.gui.util.VTOptionDefines.IGNORE_EXCLUDED_MARKUP_ITEMS; -import static ghidra.feature.vt.gui.util.VTOptionDefines.IGNORE_INCOMPLETE_MARKUP_ITEMS; -import static ghidra.feature.vt.gui.util.VTOptionDefines.INLINE; -import static ghidra.feature.vt.gui.util.VTOptionDefines.LABELS; -import static ghidra.feature.vt.gui.util.VTOptionDefines.MAX_CONFLICTS_OPTION; -import static ghidra.feature.vt.gui.util.VTOptionDefines.MIN_VOTES_OPTION; -import static ghidra.feature.vt.gui.util.VTOptionDefines.NO_RETURN; -import static ghidra.feature.vt.gui.util.VTOptionDefines.PARAMETER_COMMENTS; -import static ghidra.feature.vt.gui.util.VTOptionDefines.PARAMETER_DATA_TYPES; -import static ghidra.feature.vt.gui.util.VTOptionDefines.PARAMETER_NAMES; -import static ghidra.feature.vt.gui.util.VTOptionDefines.PARAMETER_NAMES_REPLACE_IF_SAME_PRIORITY; -import static ghidra.feature.vt.gui.util.VTOptionDefines.PLATE_COMMENT; -import static ghidra.feature.vt.gui.util.VTOptionDefines.POST_COMMENT; -import static ghidra.feature.vt.gui.util.VTOptionDefines.PRE_COMMENT; -import static ghidra.feature.vt.gui.util.VTOptionDefines.REF_CORRELATOR_MIN_CONF_OPTION; -import static ghidra.feature.vt.gui.util.VTOptionDefines.REF_CORRELATOR_MIN_SCORE_OPTION; -import static ghidra.feature.vt.gui.util.VTOptionDefines.REPEATABLE_COMMENT; -import static ghidra.feature.vt.gui.util.VTOptionDefines.RUN_DUPE_FUNCTION_OPTION; -import static ghidra.feature.vt.gui.util.VTOptionDefines.RUN_EXACT_DATA_OPTION; -import static ghidra.feature.vt.gui.util.VTOptionDefines.RUN_EXACT_FUNCTION_BYTES_OPTION; -import static ghidra.feature.vt.gui.util.VTOptionDefines.RUN_EXACT_FUNCTION_INST_OPTION; -import static ghidra.feature.vt.gui.util.VTOptionDefines.RUN_EXACT_SYMBOL_OPTION; -import static ghidra.feature.vt.gui.util.VTOptionDefines.RUN_REF_CORRELATORS_OPTION; -import static ghidra.feature.vt.gui.util.VTOptionDefines.SYMBOL_CORRELATOR_MIN_LEN_OPTION; -import static ghidra.feature.vt.gui.util.VTOptionDefines.VAR_ARGS; +import static ghidra.feature.vt.gui.actions.TableSelectionTrackingState.*; +import static ghidra.feature.vt.gui.plugin.VTPlugin.*; +import static ghidra.feature.vt.gui.util.VTOptionDefines.*; -import java.awt.Adjustable; -import java.awt.BorderLayout; -import java.awt.Dimension; -import java.awt.Rectangle; -import java.awt.event.FocusAdapter; -import java.awt.event.FocusEvent; -import java.awt.event.MouseEvent; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; +import java.awt.*; +import java.awt.event.*; +import java.util.*; import java.util.List; -import java.util.Set; -import javax.swing.BorderFactory; -import javax.swing.JButton; -import javax.swing.JComponent; -import javax.swing.JPanel; -import javax.swing.JScrollBar; -import javax.swing.JTable; -import javax.swing.ListSelectionModel; +import javax.swing.*; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; -import javax.swing.table.TableCellRenderer; -import javax.swing.table.TableColumn; -import javax.swing.table.TableColumnModel; +import javax.swing.table.*; -import docking.ActionContext; -import docking.DockingWindowManager; -import docking.WindowPosition; -import docking.widgets.table.AbstractSortedTableModel; -import docking.widgets.table.GTable; -import docking.widgets.table.RowObjectSelectionManager; -import docking.widgets.table.RowObjectTableModel; -import docking.widgets.table.SelectionManager; +import docking.*; +import docking.widgets.table.*; import docking.widgets.table.threaded.ThreadedTableModel; import ghidra.feature.vt.api.impl.VTChangeManager; import ghidra.feature.vt.api.impl.VersionTrackingChangeRecord; -import ghidra.feature.vt.api.main.VTMarkupItem; -import ghidra.feature.vt.api.main.VTMatch; -import ghidra.feature.vt.api.main.VTSession; -import ghidra.feature.vt.gui.actions.AcceptMatchAction; -import ghidra.feature.vt.gui.actions.ApplyBlockedMatchAction; -import ghidra.feature.vt.gui.actions.ApplyMatchAction; -import ghidra.feature.vt.gui.actions.ChooseMatchTagAction; -import ghidra.feature.vt.gui.actions.ClearMatchAction; -import ghidra.feature.vt.gui.actions.CreateSelectionAction; -import ghidra.feature.vt.gui.actions.EditAllTagsAction; -import ghidra.feature.vt.gui.actions.MatchTableSelectionAction; -import ghidra.feature.vt.gui.actions.RejectMatchAction; -import ghidra.feature.vt.gui.actions.RemoveMatchAction; -import ghidra.feature.vt.gui.actions.RemoveMatchTagAction; -import ghidra.feature.vt.gui.actions.TableSelectionTrackingState; +import ghidra.feature.vt.api.main.*; +import ghidra.feature.vt.gui.actions.*; import ghidra.feature.vt.gui.editors.MatchTagCellEditor; -import ghidra.feature.vt.gui.filters.AncillaryFilterDialogComponentProvider; -import ghidra.feature.vt.gui.filters.Filter; +import ghidra.feature.vt.gui.filters.*; import ghidra.feature.vt.gui.filters.Filter.FilterEditingStatus; -import ghidra.feature.vt.gui.filters.FilterDialogModel; -import ghidra.feature.vt.gui.filters.FilterStatusListener; -import ghidra.feature.vt.gui.plugin.VTController; -import ghidra.feature.vt.gui.plugin.VTControllerListener; -import ghidra.feature.vt.gui.plugin.VTPlugin; -import ghidra.feature.vt.gui.plugin.VersionTrackingPluginPackage; -import ghidra.feature.vt.gui.util.AbstractVTMatchTableModel.DestinationLabelTableColumn; -import ghidra.feature.vt.gui.util.AbstractVTMatchTableModel.SourceLabelTableColumn; -import ghidra.feature.vt.gui.util.AbstractVTMatchTableModel.StatusTableColumn; -import ghidra.feature.vt.gui.util.AbstractVTMatchTableModel.TagTableColumn; -import ghidra.feature.vt.gui.util.AllTextFilter; -import ghidra.feature.vt.gui.util.FilterIconFlashTimer; -import ghidra.feature.vt.gui.util.MatchInfo; -import ghidra.feature.vt.gui.util.MatchStatusRenderer; -import ghidra.feature.vt.gui.util.VTSymbolRenderer; -import ghidra.framework.model.DomainObject; -import ghidra.framework.model.DomainObjectChangeRecord; -import ghidra.framework.model.DomainObjectChangedEvent; +import ghidra.feature.vt.gui.plugin.*; +import ghidra.feature.vt.gui.util.*; +import ghidra.feature.vt.gui.util.AbstractVTMatchTableModel.*; +import ghidra.framework.model.*; import ghidra.framework.options.Options; import ghidra.framework.options.SaveState; import ghidra.framework.plugintool.ComponentProviderAdapter; @@ -828,26 +703,28 @@ public class VTMatchTableProvider extends ComponentProviderAdapter "should become ignored by applying a match."); vtOptions.getOptions(APPLY_MARKUP_OPTIONS_NAME) - .registerOptionsEditor(new ApplyMarkupPropertyEditor(controller)); + .registerOptionsEditor(() -> new ApplyMarkupPropertyEditor(controller)); vtOptions.getOptions(DISPLAY_APPLY_MARKUP_OPTIONS) .setOptionsHelpLocation( new HelpLocation("VersionTracking", "Apply Markup Options")); - // Auto VT options // put checkboxes to determine which correlators to run during auto VT vtOptions.registerOption(CREATE_IMPLIED_MATCHES_OPTION, true, null, "Create Implied Matches when AutoVT correlators apply function matches."); - vtOptions.registerOption(RUN_EXACT_DATA_OPTION, true, null, "Run the Exact Data Correlator"); - vtOptions.registerOption(RUN_EXACT_SYMBOL_OPTION, true, null, "Run the Exact Symbol Correlator"); + vtOptions.registerOption(RUN_EXACT_DATA_OPTION, true, null, + "Run the Exact Data Correlator"); + vtOptions.registerOption(RUN_EXACT_SYMBOL_OPTION, true, null, + "Run the Exact Symbol Correlator"); vtOptions.registerOption(RUN_EXACT_FUNCTION_BYTES_OPTION, true, null, "Run the Exact Function Bytes Correlator"); vtOptions.registerOption(RUN_EXACT_FUNCTION_INST_OPTION, true, null, "Run the Exact Function Instruction Bytes and Mnemonics Correlators"); vtOptions.registerOption(RUN_DUPE_FUNCTION_OPTION, true, null, "Run the Duplicate Function Instruction Correlator"); - vtOptions.registerOption(RUN_REF_CORRELATORS_OPTION, true, null, "Run the Reference Correlators"); + vtOptions.registerOption(RUN_REF_CORRELATORS_OPTION, true, null, + "Run the Reference Correlators"); // set help for the sub categories diff --git a/Ghidra/Framework/Graph/src/main/java/ghidra/service/graph/GraphDisplayOptions.java b/Ghidra/Framework/Graph/src/main/java/ghidra/service/graph/GraphDisplayOptions.java index df4ad9bae3..a17ec64b69 100644 --- a/Ghidra/Framework/Graph/src/main/java/ghidra/service/graph/GraphDisplayOptions.java +++ b/Ghidra/Framework/Graph/src/main/java/ghidra/service/graph/GraphDisplayOptions.java @@ -111,6 +111,7 @@ public class GraphDisplayOptions implements OptionsChangeListener { * {@link GraphType}s that have options registered in the tool. * @param graphType The {@link GraphType} for which to define display options * @param tool the tool from which to initialize from {@link ToolOptions} + * @param help the help location */ protected GraphDisplayOptions(GraphType graphType, Tool tool, HelpLocation help) { this(graphType); @@ -380,7 +381,7 @@ public class GraphDisplayOptions implements OptionsChangeListener { /** * Sets the color for vertices with the given vertex type. Note that this method does not - * allow the vertex color to be registered in tool options. + * allow the vertex color to be registered in tool options. * See {@link #setVertexColor(String, String)}. * @param vertexType the vertex type for which to set its color * @param color the color to use for vertices with the given vertex type @@ -392,7 +393,7 @@ public class GraphDisplayOptions implements OptionsChangeListener { /** * Sets the vertex color using a theme color id. By using a theme color id, this property - * is eligible to be registered as a tool option. + * is eligible to be registered as a tool option. * @param vertexType the vertex type for which to set its color * @param themeColorId the theme color id of the color for this vertex type */ @@ -428,7 +429,7 @@ public class GraphDisplayOptions implements OptionsChangeListener { /** * Sets the edge color using a theme color id. By using a theme color id, this property - * is eligible to be registered as a tool option. + * is eligible to be registered as a tool option. * @param edgeType the edge type for which to set its color * @param themeColorId the theme color id of the color for this edge type */ @@ -574,7 +575,7 @@ public class GraphDisplayOptions implements OptionsChangeListener { /** * Returns true if the rendering mode is to use icons for the vertices. If using - * icons, the label is drawn inside the shape. + * icons, the label is drawn inside the shape. * @return true if the rendering mode is to use icons. */ public boolean usesIcons() { @@ -592,7 +593,7 @@ public class GraphDisplayOptions implements OptionsChangeListener { } /** - * Returns the label position relative to the vertex. Note this is only relevant + * Returns the label position relative to the vertex. Note this is only relevant * if {@link #usesIcons()} is false * @return the label position relative to the vertex */ @@ -659,8 +660,8 @@ public class GraphDisplayOptions implements OptionsChangeListener { } /** - * Sets the maximum number of nodes a graph can have and still be displayed. Be careful, - * setting this value too high can result in Ghidra running out of memory and/or + * Sets the maximum number of nodes a graph can have and still be displayed. Be careful, + * setting this value too high can result in Ghidra running out of memory and/or * making the system very sluggish. * @param maxNodeCount the maximum number of nodes a graph can have and still be displayed. */ @@ -705,7 +706,7 @@ public class GraphDisplayOptions implements OptionsChangeListener { * are registered with tool options, show the tool options with the appropriate * graph options selected. Otherwise, show an editor for locally editing these * options. - * @param tool the tool + * @param tool the tool * @param help the help location to use if the options are edited locally */ public void displayEditor(Tool tool, HelpLocation help) { @@ -723,7 +724,7 @@ public class GraphDisplayOptions implements OptionsChangeListener { } // Otherwise, create a new empty options, register the graph options into the - // those options and use our options editor on those options to allow the + // those options and use our options editor on those options to allow the // user to change these graph display options. ToolOptions transientOptions = new ToolOptions("Graph"); @@ -740,7 +741,7 @@ public class GraphDisplayOptions implements OptionsChangeListener { } /** - * Sets default values for vertex types. This method does not allow the vertexType color to + * Sets default values for vertex types. This method does not allow the vertexType color to * be eligible to be registered as a tool option. * @param vertexType the vertex type whose default color and shape are being defined * @param vertexShape the default vertex shape for the given vertex type @@ -765,7 +766,7 @@ public class GraphDisplayOptions implements OptionsChangeListener { } /** - * Sets default values for edge types. This method does not allow the vertexType color to + * Sets default values for edge types. This method does not allow the vertexType color to * be eligible to be registered as a tool option. * @param edgeType the edge type whose default color and shape are being defined * @param color the default color for the given edge type @@ -879,8 +880,7 @@ public class GraphDisplayOptions implements OptionsChangeListener { } } List list = new ArrayList<>(graphType.getVertexTypes()); - OptionsEditor editor = new ScrollableOptionsEditor(VERTEX_COLORS, list); - options.registerOptionsEditor(editor); + options.registerOptionsEditor(() -> new ScrollableOptionsEditor(VERTEX_COLORS, list)); } private void registerVertexShapeOptions(Options rootOptions, HelpLocation help) { @@ -889,14 +889,12 @@ public class GraphDisplayOptions implements OptionsChangeListener { List shapeNames = VertexShape.getShapeNames(); for (String vertexType : graphType.getVertexTypes()) { - StringWithChoicesEditor editor = new StringWithChoicesEditor(shapeNames); options.registerOption(vertexType, OptionType.STRING_TYPE, getVertexShapeName(vertexType), help, "Choose the shape for this vertex type", - editor); + () -> new StringWithChoicesEditor(shapeNames)); } List list = new ArrayList<>(graphType.getVertexTypes()); - OptionsEditor editor = new ScrollableOptionsEditor(VERTEX_SHAPES, list); - options.registerOptionsEditor(editor); + options.registerOptionsEditor(() -> new ScrollableOptionsEditor(VERTEX_SHAPES, list)); } private void registerEdgeColorOptions(Options rootOptions, HelpLocation help) { @@ -909,8 +907,7 @@ public class GraphDisplayOptions implements OptionsChangeListener { } } List list = new ArrayList<>(graphType.getEdgeTypes()); - OptionsEditor editor = new ScrollableOptionsEditor(EDGE_COLORS, list); - options.registerOptionsEditor(editor); + options.registerOptionsEditor(() -> new ScrollableOptionsEditor(EDGE_COLORS, list)); } private void registerMiscellaneousOptions(Options rootOptions, HelpLocation help) { @@ -921,7 +918,6 @@ public class GraphDisplayOptions implements OptionsChangeListener { optionNamesInDisplayOrder.add(MAX_NODES_SIZE); options.registerOption(MAX_NODES_SIZE, OptionType.INT_TYPE, maxNodeCount, help, "Graphs with more than this number of nodes will not be displayed. (Large graphs can cause Ghidra to become unstable/sluggish)"); - StringWithChoicesEditor editor = new StringWithChoicesEditor(VertexShape.getShapeNames()); if (defaultRegistrations.containsKey(VERTEX_SELECTION_COLOR)) { optionNamesInDisplayOrder.add(VERTEX_SELECTION_COLOR); @@ -954,20 +950,21 @@ public class GraphDisplayOptions implements OptionsChangeListener { optionNamesInDisplayOrder.add(DEFAULT_VERTEX_SHAPE); options.registerOption(DEFAULT_VERTEX_SHAPE, OptionType.STRING_TYPE, defaultVertexShape.getName(), help, - "Shape for vertices that have no vertex type defined", editor); + "Shape for vertices that have no vertex type defined", + () -> new StringWithChoicesEditor(VertexShape.getShapeNames())); optionNamesInDisplayOrder.add(FAVORED_EDGE_TYPE); List edgeTypes = graphType.getEdgeTypes(); if (!edgeTypes.isEmpty()) { - editor = new StringWithChoicesEditor(edgeTypes); options.registerOption(FAVORED_EDGE_TYPE, OptionType.STRING_TYPE, favoredEdgeType, help, - "Favored edge is used to influence layout algorithms", editor); + "Favored edge is used to influence layout algorithms", + () -> new StringWithChoicesEditor(edgeTypes)); } optionNamesInDisplayOrder.add(DEFAULT_LAYOUT_ALGORITHM); - editor = new StringWithChoicesEditor(LayoutAlgorithmNames.getLayoutAlgorithmNames()); options.registerOption(DEFAULT_LAYOUT_ALGORITHM, OptionType.STRING_TYPE, - defaultLayoutAlgorithmName, help, "Initial layout algorithm", editor); + defaultLayoutAlgorithmName, help, "Initial layout algorithm", + () -> new StringWithChoicesEditor(LayoutAlgorithmNames.getLayoutAlgorithmNames())); optionNamesInDisplayOrder.add(LABEL_POSITION); options.registerOption(LABEL_POSITION, OptionType.ENUM_TYPE, labelPosition, help, @@ -982,11 +979,8 @@ public class GraphDisplayOptions implements OptionsChangeListener { optionNamesInDisplayOrder.add(USE_ICONS); options.registerOption(USE_ICONS, OptionType.BOOLEAN_TYPE, useIcons, help, "If true, vertices are drawn using pre-rendered images versus compact shapes"); - - OptionsEditor optionsEditor = - new ScrollableOptionsEditor(MISCELLANEOUS_OPTIONS, optionNamesInDisplayOrder); - options.registerOptionsEditor(optionsEditor); - + options.registerOptionsEditor( + () -> new ScrollableOptionsEditor(MISCELLANEOUS_OPTIONS, optionNamesInDisplayOrder)); } private void checkVertexType(String vertexType) { diff --git a/Ghidra/Framework/Gui/src/main/java/ghidra/framework/options/AbstractOptions.java b/Ghidra/Framework/Gui/src/main/java/ghidra/framework/options/AbstractOptions.java index c1c4454790..fb3d854c6d 100644 --- a/Ghidra/Framework/Gui/src/main/java/ghidra/framework/options/AbstractOptions.java +++ b/Ghidra/Framework/Gui/src/main/java/ghidra/framework/options/AbstractOptions.java @@ -21,17 +21,17 @@ import java.beans.PropertyEditor; import java.beans.PropertyEditorManager; import java.io.File; import java.util.*; +import java.util.function.Supplier; import javax.swing.KeyStroke; -import javax.swing.SwingUtilities; import generic.theme.*; -import ghidra.util.HelpLocation; -import ghidra.util.Msg; +import ghidra.util.*; import ghidra.util.datastruct.WeakDataStructureFactory; import ghidra.util.datastruct.WeakSet; import ghidra.util.exception.AssertException; import utilities.util.reflection.ReflectionUtilities; +import utility.function.Dummy; public abstract class AbstractOptions implements Options { public static final Set> SUPPORTED_CLASSES = buildSupportedClassSet(); @@ -83,7 +83,14 @@ public abstract class AbstractOptions implements Options { protected abstract boolean notifyOptionChanged(String optionName, Object oldValue, Object newValue); - public synchronized void registerOptionsEditor(String categoryPath, OptionsEditor editor) { + public synchronized void registerOptionsEditor(String categoryPath, + Supplier editorSupplier) { + + OptionsEditor editor = null; + if (!SystemUtilities.isInHeadlessMode()) { + editor = editorSupplier.get(); + } + optionsEditorMap.put(categoryPath, editor); } @@ -128,12 +135,13 @@ public abstract class AbstractOptions implements Options { @Override public void registerOption(String optionName, OptionType type, Object defaultValue, HelpLocation help, String description) { - registerOption(optionName, type, defaultValue, help, description, null); + registerOption(optionName, type, defaultValue, help, description, + (Supplier) null); } @Override public synchronized void registerOption(String optionName, OptionType type, Object defaultValue, - HelpLocation help, String description, PropertyEditor editor) { + HelpLocation help, String description, Supplier editorSupplier) { if (type == OptionType.NO_TYPE) { throw new IllegalArgumentException( @@ -153,10 +161,20 @@ public abstract class AbstractOptions implements Options { ", defaultValue = " + defaultValue); } - if (type == OptionType.CUSTOM_TYPE && editor == null) { - throw new IllegalStateException( - "Can't register a custom option without a property editor"); + editorSupplier = Dummy.ifNull(editorSupplier); + PropertyEditor editor = null; + boolean isHeadless = SystemUtilities.isInHeadlessMode(); + if (!isHeadless) { + editor = editorSupplier.get(); } + + if (type == OptionType.CUSTOM_TYPE) { + if (!isHeadless && editor == null) { + throw new IllegalStateException( + "Can't register a custom option without a property editor"); + } + } + if (description == null) { Msg.error(this, "Registered an option without a description: " + optionName, ReflectionUtilities.createJavaFilteredThrowable()); @@ -169,7 +187,8 @@ public abstract class AbstractOptions implements Options { } Option option = - createRegisteredOption(optionName, type, description, help, defaultValue, editor); + createRegisteredOption(optionName, type, description, help, defaultValue, + editor); valueMap.put(optionName, option); } @@ -594,8 +613,8 @@ public abstract class AbstractOptions implements Options { @Override public PropertyEditor getPropertyEditor(String optionName) { - if (!SwingUtilities.isEventDispatchThread()) { - throw new IllegalStateException("This method must be called from the swing thread."); + if (!Swing.isSwingThread()) { + throw new IllegalStateException("This method must be called from the Swing thread"); } Option option = getOption(optionName, OptionType.NO_TYPE, null); PropertyEditor editor = option.getPropertyEditor(); @@ -688,8 +707,8 @@ public abstract class AbstractOptions implements Options { } @Override - public synchronized void registerOptionsEditor(OptionsEditor editor) { - optionsEditorMap.put("", editor); + public synchronized void registerOptionsEditor(Supplier editor) { + registerOptionsEditor("", editor); } @Override diff --git a/Ghidra/Framework/Gui/src/main/java/ghidra/framework/options/Options.java b/Ghidra/Framework/Gui/src/main/java/ghidra/framework/options/Options.java index 821431476d..14c713cebe 100644 --- a/Ghidra/Framework/Gui/src/main/java/ghidra/framework/options/Options.java +++ b/Ghidra/Framework/Gui/src/main/java/ghidra/framework/options/Options.java @@ -20,10 +20,13 @@ import java.awt.Font; import java.beans.PropertyEditor; import java.io.File; import java.util.*; +import java.util.function.Supplier; import javax.swing.KeyStroke; import ghidra.util.HelpLocation; +import ghidra.util.Msg; +import utilities.util.reflection.ReflectionUtilities; public interface Options { public static final char DELIMITER = '.'; @@ -108,11 +111,11 @@ public interface Options { /** * Registers an option with a description, help location, and a default value without specifying * the option type. This form requires that the default value not be null so that the option - * type can be inferred from the default value. + * type can be inferred from the default value. *

* Note, this method should not be used for * colors and font as doing so will result in those colors and fonts becoming disconnected - * to the current theme. Instead use + * to the current theme. Instead use * * {@link #registerThemeColorBinding(String, String, HelpLocation, String)} or * {@link #registerThemeFontBinding(String, String, HelpLocation, String)}. @@ -133,7 +136,7 @@ public interface Options { *

* Note, this method should not be used for * colors and font as doing so will result in those colors and fonts becoming disconnected - * to the current theme. Instead use + * to the current theme. Instead use * {@link #registerThemeColorBinding(String, String, HelpLocation, String)} or * {@link #registerThemeFontBinding(String, String, HelpLocation, String)}. * @@ -153,9 +156,16 @@ public interface Options { *

* Note, this method should not be used for * colors and font as doing so will result in those colors and fonts becoming disconnected - * to the current theme. Instead use + * to the current theme. Instead use * {@link #registerThemeColorBinding(String, String, HelpLocation, String)} or * {@link #registerThemeFontBinding(String, String, HelpLocation, String)}. + *

+ * Note: we use a supplier of a custom editor, instead of a custom editor, to avoid + * creating {@link PropertyEditor}s until needed. This allows us to use the same API in both + * GUI mode and headless mode. If GUI property editors are created in headless mode, exceptions + * may be thrown. This API will not use the supplier when in headless mode, this avoiding the + * creation of GUI components. For this to work correctly, clients using custom property + * editors must defer construction of the editor until the supplier is called. * * @param optionName the name of the option being registered. * @param type the OptionType for this options. @@ -163,12 +173,37 @@ public interface Options { * value may be null. * @param help the HelpLocation for this option. * @param description a description of the option. - * @param editor an optional custom editor for this property. Note if the option is a custom option, - * then the property editor can't be null; + * @param editor an optional supplier of a custom editor for this property. Note if the option + * is a custom option, then the property editor can't be null; * @throws IllegalStateException if the options is a custom option and the editor is null. */ public void registerOption(String optionName, OptionType type, Object defaultValue, - HelpLocation help, String description, PropertyEditor editor); + HelpLocation help, String description, Supplier editor); + + /** + * Use instead + * {@link #registerOption(String, OptionType, Object, HelpLocation, String, Supplier)} + * @param optionName the name of the option being registered. + * @param type the OptionType for this options. + * @param defaultValue the defaultValue for the option. In this version of the method, the default + * value may be null. + * @param help the HelpLocation for this option. + * @param description a description of the option. + * @param editor an optional supplier of a custom editor for this property. Note if the option + * is a custom option, then the property editor can't be null; + * @deprecated Use instead + * {@link #registerOption(String, OptionType, Object, HelpLocation, String, Supplier)} + */ + @Deprecated(since = "11.1", forRemoval = true) + default public void registerOption(String optionName, OptionType type, Object defaultValue, + HelpLocation help, String description, PropertyEditor editor) { + String caller = ReflectionUtilities.getClassNameOlderThan(Options.class); + String message = """ + Using deprecated call to registerOption(PropertyEditor) from % for option %s + """.formatted(caller, optionName); + Msg.debug(this, message); + registerOption(optionName, type, defaultValue, help, description, () -> editor); + } /** * Register/binds the option to a theme color id. Changing the option's color via the options @@ -185,7 +220,7 @@ public interface Options { * Register/binds the option to a theme font id. Changing the option's font via the options * Gui will result in directly changing the theme color of the given font id. * @param optionName the name of the font option - * @param fontId the theme color id whose color value is changed when the option's color + * @param fontId the theme color id whose color value is changed when the option's color * is changed * @param help the HelpLocation for this option * @param description a description of the option @@ -194,10 +229,34 @@ public interface Options { String description); /** - * Register the options editor that will handle the editing for all the options or a sub group of options. - * @param editor the custom editor panel to be used to edit the options or sub group of options. + * Register the options editor that will handle the editing for all the options or a sub-group + * of options. + *

+ * Note: we use a supplier of a custom editor, instead of a custom editor, to avoid + * creating {@link PropertyEditor}s until needed. This allows us to use the same API in both + * GUI mode and headless mode. If GUI property editors are created in headless mode, exceptions + * may be thrown. This API will not use the supplier when in headless mode, this avoiding the + * creation of GUI components. For this to work correctly, clients using custom property + * editors must defer construction of the editor until the supplier is called. + * @param editor a supplier for the custom editor panel to be used to edit the options or + * sub-group of options. */ - public void registerOptionsEditor(OptionsEditor editor); + public void registerOptionsEditor(Supplier editor); + + /** + * Use instead {@link #registerOptionsEditor(Supplier)} + * @param editor the editor + * @deprecated Use instead {@link #registerOptionsEditor(Supplier)} + */ + @Deprecated(since = "11.1", forRemoval = true) + default public void registerOptionsEditor(OptionsEditor editor) { + String caller = ReflectionUtilities.getClassNameOlderThan(Options.class); + String message = """ + Using deprecated call to registerOption(OptionsEditor) from % for options %s + """.formatted(caller, getName()); + Msg.debug(this, message); + registerOptionsEditor(() -> editor); + } /** * Get the editor that will handle editing all the values in this options or sub group of options. diff --git a/Ghidra/Framework/Gui/src/main/java/ghidra/framework/options/SubOptions.java b/Ghidra/Framework/Gui/src/main/java/ghidra/framework/options/SubOptions.java index 53b72c9fdb..8a7cea7229 100644 --- a/Ghidra/Framework/Gui/src/main/java/ghidra/framework/options/SubOptions.java +++ b/Ghidra/Framework/Gui/src/main/java/ghidra/framework/options/SubOptions.java @@ -20,6 +20,7 @@ import java.awt.Font; import java.beans.PropertyEditor; import java.io.File; import java.util.*; +import java.util.function.Supplier; import javax.swing.KeyStroke; @@ -61,7 +62,7 @@ public class SubOptions implements Options { public List getChildOptions() { List optionPaths = getOptionNames(); Set childCategories = AbstractOptions.getChildCategories(optionPaths); - List childOptions = new ArrayList(childCategories.size()); + List childOptions = new ArrayList<>(childCategories.size()); for (String categoryName : childCategories) { childOptions.add(new SubOptions(options, categoryName, prefix + categoryName + DELIMITER)); @@ -88,7 +89,7 @@ public class SubOptions implements Options { @Override public void registerOption(String optionName, OptionType type, Object defaultValue, - HelpLocation help, String description, PropertyEditor editor) { + HelpLocation help, String description, Supplier editor) { options.registerOption(prefix + optionName, type, defaultValue, help, description, editor); } @@ -252,7 +253,7 @@ public class SubOptions implements Options { @Override public List getOptionNames() { List allOptionPaths = options.getOptionNames(); - List names = new ArrayList(); + List names = new ArrayList<>(); for (String path : allOptionPaths) { if (path.startsWith(prefix)) { names.add(path.substring(prefix.length())); @@ -317,7 +318,7 @@ public class SubOptions implements Options { } @Override - public void registerOptionsEditor(OptionsEditor editor) { + public void registerOptionsEditor(Supplier editor) { options.registerOptionsEditor(prefix, editor); } @@ -378,7 +379,7 @@ public class SubOptions implements Options { public List getLeafOptionNames() { List optionPaths = getOptionNames(); Set leaves = AbstractOptions.getLeaves(optionPaths); - return new ArrayList(leaves); + return new ArrayList<>(leaves); } @Override diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/mgr/OptionsManager.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/mgr/OptionsManager.java index 2ddef1377a..e93ba220d5 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/mgr/OptionsManager.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/mgr/OptionsManager.java @@ -238,7 +238,7 @@ public class OptionsManager implements OptionsService, OptionsChangeListener { oldEditor.dispose(); } - keyBindingOptions.registerOptionsEditor(new KeyBindingOptionsEditor()); + keyBindingOptions.registerOptionsEditor(() -> new KeyBindingOptionsEditor()); OptionsDialog dialog = new OptionsDialog("Options for " + tool.getName(), "Options", getEditableOptions(), null, true); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramCompilerSpec.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramCompilerSpec.java index a086a0fd89..91c4373868 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramCompilerSpec.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramCompilerSpec.java @@ -363,7 +363,7 @@ public class ProgramCompilerSpec extends BasicCompilerSpec { OptionType.STRING_TYPE, evalChoices[0], new HelpLocation("DecompilePlugin", "OptionProtoEval"), "Select the default function prototype/evaluation model to be used during Decompiler analysis", - new StringWithChoicesEditor(evalChoices)); + () -> new StringWithChoicesEditor(evalChoices)); // TODO: registration of DECOMPILER_OUTPUT_LANGUAGE option should be tied to Processor // and not presence of stored option. diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/SpecExtension.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/SpecExtension.java index 8589e7d180..e89f614cfb 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/SpecExtension.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/SpecExtension.java @@ -36,7 +36,7 @@ import ghidra.xml.*; * Utility class for installing/removing "specification extensions" to a Program. * A specification extension is a program specific version of either a: * - Prototype Model - * - Call Fixup or + * - Call Fixup or * - Callother Fixup * Normally these objects are provided by the language specific configuration files (.cspec or .pspec), * but this class allows additional objects to be added that are specific to the program. @@ -48,7 +48,7 @@ import ghidra.xml.*; * - \ - describing a Callother Fixup * - \ - describing a typical Prototype Model * - \ - describing a Prototype Model merged from other models - * + * * Each type of object has a unique name or target, which must be specified as part of the XML tag, * which is referred to in this class as the extension's "formal name". In the \ tag, * the formal name is given by the "targetop" attribute; for all the other tags, the formal name is @@ -332,7 +332,7 @@ public class SpecExtension { if (!SystemUtilities.isInHeadlessMode()) { Options options = program.getOptions(SPEC_EXTENSION); options.setOptionsHelpLocation(new HelpLocation("DecompilePlugin", "ExtensionOptions")); - options.registerOptionsEditor(new SpecExtensionEditor((ProgramDB) program)); + options.registerOptionsEditor(() -> new SpecExtensionEditor((ProgramDB) program)); } }