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 480b47a310..ed3362a521 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 @@ -84,12 +84,11 @@ public class AutoAnalysisPlugin extends Plugin implements AutoAnalysisManagerLis // get the option so that an owner is associated with it, otherwise // it will not show up in the Options dialog for the tool. Options options = tool.getOptions(GhidraOptions.CATEGORY_AUTO_ANALYSIS); - String description = - "This option forces the analysis options" - + " dialog to appear whenever auto-analysis action is invoked."; + String description = "This option forces the analysis options" + + " dialog to appear whenever auto-analysis action is invoked."; helpLocation = new HelpLocation("AutoAnalysisPlugin", "AnalysisOptions"); - + options.setOptionsHelpLocation(helpLocation); options.registerOption(SHOW_ANALYSIS_OPTIONS, true, helpLocation, description); } @@ -266,8 +265,8 @@ public class AutoAnalysisPlugin extends Plugin implements AutoAnalysisManagerLis Options options = program.getOptions(Program.ANALYSIS_PROPERTIES); options.registerOptionsEditor(new AnalysisOptionsEditor(program)); - options.setOptionsHelpLocation(new HelpLocation("AutoAnalysisPlugin", - "Auto_Analysis_Option")); + options.setOptionsHelpLocation( + new HelpLocation("AutoAnalysisPlugin", "Auto_Analysis_Option")); } private void programActivated(final Program program) { @@ -316,9 +315,8 @@ public class AutoAnalysisPlugin extends Plugin implements AutoAnalysisManagerLis public void analysisEnded(AutoAnalysisManager manager) { MessageLog log = manager.getMessageLog(); if (log.getMsgCount() > 0) { - MultiLineMessageDialog dialog = - new MultiLineMessageDialog("Auto Analysis Summary", - "There were warnings/errors issued during analysis.", log.toString(), + MultiLineMessageDialog dialog = new MultiLineMessageDialog("Auto Analysis Summary", + "There were warnings/errors issued during analysis.", log.toString(), MultiLineMessageDialog.WARNING_MESSAGE, false);//modal? DockingWindowManager.showDialog(null, dialog); } @@ -332,9 +330,8 @@ public class AutoAnalysisPlugin extends Plugin implements AutoAnalysisManagerLis public OneShotAnalyzerAction(Analyzer analyzer) { super(analyzer.getName(), AutoAnalysisPlugin.this.getName()); this.analyzer = analyzer; - setMenuBarData(new MenuData( - new String[] { "Analysis", "One Shot", analyzer.getName() }, null, - ANALYZE_GROUP_NAME)); + setMenuBarData(new MenuData(new String[] { "Analysis", "One Shot", analyzer.getName() }, + null, ANALYZE_GROUP_NAME)); setHelpLocation(new HelpLocation("AutoAnalysisPlugin", "Auto_Analyzers")); setEnabled(false); 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 b37a6752b9..1a84862181 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 @@ -71,7 +71,7 @@ public class ArrayValuesFieldFactory extends FieldFactory { FormatManager.ARRAY_DISPLAY_OPTIONS, new ArrayElementWrappedOption()); HelpLocation hl = new HelpLocation("CodeBrowserPlugin", "Array_Options"); - fieldOptions.getOptions(FormatManager.OPTIONS_GROUP).setOptionsHelpLocation(hl); + fieldOptions.getOptions(FormatManager.ARRAY_OPTIONS_GROUP).setOptionsHelpLocation(hl); fieldOptions.getOptions(FormatManager.ARRAY_DISPLAY_OPTIONS).setOptionsHelpLocation(hl); if (!(wrappedOption instanceof ArrayElementWrappedOption)) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/FieldNameFieldFactory.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/FieldNameFieldFactory.java index 48460669b7..eb1bc69114 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/FieldNameFieldFactory.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/FieldNameFieldFactory.java @@ -23,6 +23,7 @@ import docking.widgets.fieldpanel.support.FieldLocation; import ghidra.GhidraOptions; import ghidra.app.util.HighlightProvider; import ghidra.app.util.viewer.format.FieldFormatModel; +import ghidra.app.util.viewer.format.FormatManager; import ghidra.app.util.viewer.options.OptionsGui; import ghidra.app.util.viewer.proxy.ProxyObj; import ghidra.framework.options.Options; @@ -34,16 +35,14 @@ import ghidra.program.util.FieldNameFieldLocation; import ghidra.program.util.ProgramLocation; /** - * Generates Data Field name Fields. + * Generates Data Field (structure field names and array indexes) name Fields. */ public class FieldNameFieldFactory extends FieldFactory { public static final String FIELD_NAME = "Field Name"; - private final static String GROUP_TITLE = FIELD_NAME; - - public final static String ARRAY_INDEX_FORMAT_MSG = - GROUP_TITLE + Options.DELIMITER + "Array Index Format"; + public final static String ARRAY_INDEX_FORMAT_NAME = + FormatManager.ARRAY_OPTIONS_GROUP + Options.DELIMITER + "Array Index Format"; public static enum IndexFormat { @@ -62,9 +61,6 @@ public class FieldNameFieldFactory extends FieldFactory { private IndexFormat format; - /** - * Default constructor - */ public FieldNameFieldFactory() { super(FIELD_NAME); } @@ -72,16 +68,16 @@ public class FieldNameFieldFactory extends FieldFactory { /** * Constructor * @param model the model that the field belongs to. - * @param hsProvider the HightLightStringProvider. + * @param hlProvider the HightLightStringProvider. * @param displayOptions the Options for display properties. * @param fieldOptions the Options for field specific properties. */ private FieldNameFieldFactory(FieldFormatModel model, HighlightProvider hlProvider, Options displayOptions, ToolOptions fieldOptions) { super(FIELD_NAME, model, hlProvider, displayOptions, fieldOptions); - fieldOptions.registerOption(ARRAY_INDEX_FORMAT_MSG, IndexFormat.decimal, null, - "Hex or Decimal field offsets"); - format = fieldOptions.getEnum(ARRAY_INDEX_FORMAT_MSG, IndexFormat.decimal); + fieldOptions.registerOption(ARRAY_INDEX_FORMAT_NAME, IndexFormat.decimal, null, + "Hex or Decimal field offsets for arrays"); + format = fieldOptions.getEnum(ARRAY_INDEX_FORMAT_NAME, IndexFormat.decimal); } private String getFieldName(Data data) { @@ -94,25 +90,19 @@ public class FieldNameFieldFactory extends FieldFactory { return data.getFieldName(); } - /** - * @see ghidra.app.util.viewer.field.FieldFactory#fieldOptionsChanged(ghidra.framework.options.ToolOptions, java.lang.String, java.lang.Object, java.lang.Object) - */ @Override public void fieldOptionsChanged(Options options, String optionName, Object oldValue, Object newValue) { super.fieldOptionsChanged(options, optionName, oldValue, newValue); if (options.getName().equals(GhidraOptions.CATEGORY_BROWSER_FIELDS)) { - if (optionName.equals(ARRAY_INDEX_FORMAT_MSG)) { + if (optionName.equals(ARRAY_INDEX_FORMAT_NAME)) { format = (IndexFormat) newValue; model.update(); } } } - /** - * @see ghidra.app.util.viewer.field.FieldFactory#getField(ProxyObj, int) - */ @Override public ListingField getField(ProxyObj proxy, int varWidth) { Object obj = proxy.getObject(); @@ -132,9 +122,6 @@ public class FieldNameFieldFactory extends FieldFactory { width, hlProvider); } - /** - * @see ghidra.app.util.viewer.field.FieldFactory#getProgramLocation(int, int, ghidra.app.util.viewer.field.ListingField) - */ @Override public ProgramLocation getProgramLocation(int row, int col, ListingField bf) { Object obj = bf.getProxy().getObject(); @@ -146,9 +133,6 @@ public class FieldNameFieldFactory extends FieldFactory { data.getComponentPath(), getFieldName(data), col); } - /** - * @see ghidra.app.util.viewer.field.FieldFactory#getFieldLocation(ghidra.app.util.viewer.field.ListingField, BigInteger, int, ghidra.program.util.ProgramLocation) - */ @Override public FieldLocation getFieldLocation(ListingField bf, BigInteger index, int fieldNum, ProgramLocation programLoc) { @@ -163,9 +147,6 @@ public class FieldNameFieldFactory extends FieldFactory { } - /** - * @see ghidra.app.util.viewer.field.FieldFactory#acceptsType(int, java.lang.Class) - */ @Override public boolean acceptsType(int category, Class proxyObjectClass) { if (!CodeUnit.class.isAssignableFrom(proxyObjectClass)) { @@ -176,13 +157,10 @@ public class FieldNameFieldFactory extends FieldFactory { @Override public FieldFactory newInstance(FieldFormatModel formatModel, HighlightProvider provider, - ToolOptions displayOptions, ToolOptions fieldOptions) { - return new FieldNameFieldFactory(formatModel, provider, displayOptions, fieldOptions); + ToolOptions toolOptions, ToolOptions fieldOptions) { + return new FieldNameFieldFactory(formatModel, provider, toolOptions, fieldOptions); } - /** - * @see ghidra.app.util.viewer.field.FieldFactory#getDefaultColor() - */ @Override public Color getDefaultColor() { return OptionsGui.FIELD_NAME.getDefaultColor(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/PlateFieldFactory.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/PlateFieldFactory.java index 47efa36cce..2a4e0c0711 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/PlateFieldFactory.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/PlateFieldFactory.java @@ -44,7 +44,7 @@ public class PlateFieldFactory extends FieldFactory { private static final String EMPTY_STRING = ""; public static final String FIELD_NAME = "Plate Comment"; public static final Color DEFAULT_COLOR = Color.BLUE; - private final static String FIELD_GROUP_TITLE = "Plate Comment"; + private final static String FIELD_GROUP_TITLE = "Plate Comments Field"; public final static String ENABLE_WORD_WRAP_MSG = FIELD_GROUP_TITLE + Options.DELIMITER + "Enable Word Wrapping"; @@ -107,7 +107,7 @@ public class PlateFieldFactory extends FieldFactory { /** * Constructor * @param model the model that the field belongs to. - * @param hsProvider the HightLightStringProvider. + * @param hlProvider the HightLightStringProvider. * @param displayOptions the Options for display properties. * @param fieldOptions the Options for field specific properties. */ @@ -249,7 +249,7 @@ public class PlateFieldFactory extends FieldFactory { * Generate a text line for the plate text. * Text will be left justified between two '*' and padded based upon the * available field width. - * @param text plate text string + * @param elements the field elements that may get updated * @return formatted plate text line */ private boolean addSideBorders(List elements) { @@ -504,8 +504,8 @@ public class PlateFieldFactory extends FieldFactory { @Override public FieldFactory newInstance(FieldFormatModel formatModel, HighlightProvider hsProvider, - ToolOptions displayOptions, ToolOptions fieldOptions) { - return new PlateFieldFactory(formatModel, hsProvider, displayOptions, fieldOptions); + ToolOptions toolOptions, ToolOptions fieldOptions) { + return new PlateFieldFactory(formatModel, hsProvider, toolOptions, fieldOptions); } @Override diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/PostCommentFieldFactory.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/PostCommentFieldFactory.java index bf6ee2468f..dbda07ad92 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/PostCommentFieldFactory.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/PostCommentFieldFactory.java @@ -49,7 +49,7 @@ public class PostCommentFieldFactory extends FieldFactory { public static final String FIELD_NAME = "Post-Comment"; private final static String GROUP_TITLE = "Format Code"; - private final static String FIELD_GROUP_TITLE = "Post Comment"; + private final static String FIELD_GROUP_TITLE = "Post-comments Field"; public final static String ENABLE_WORD_WRAP_MSG = FIELD_GROUP_TITLE + Options.DELIMITER + "Enable Word Wrapping"; public final static String ENABLE_ALWAYS_SHOW_AUTOMATIC_MSG = diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/PreCommentFieldFactory.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/PreCommentFieldFactory.java index 8cf9bed251..d07a2a1a1c 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/PreCommentFieldFactory.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/PreCommentFieldFactory.java @@ -46,7 +46,7 @@ public class PreCommentFieldFactory extends FieldFactory { public static final String FIELD_NAME = "Pre-Comment"; private final static String GROUP_TITLE = "Format Code"; - private final static String FIELD_GROUP_TITLE = "Pre-Comment"; + private final static String FIELD_GROUP_TITLE = "Pre-comments Field"; public final static String ENABLE_WORD_WRAP_MSG = FIELD_GROUP_TITLE + Options.DELIMITER + "Enable Word Wrapping"; public final static String ENABLE_ALWAYS_SHOW_AUTOMATIC_MSG = 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 d018aeb404..f965d22cd3 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 @@ -37,14 +37,14 @@ import ghidra.util.exception.AssertException; * Class to manage the set of format models. */ public class FormatManager implements OptionsChangeListener { - public static final String OPTIONS_GROUP = "Array Options"; + public static final String ARRAY_OPTIONS_GROUP = "Array Options"; private static final String HIGHLIGHT_GROUP = "Cursor Text Highlight"; public static final String HIGHLIGHT_COLOR_NAME = HIGHLIGHT_GROUP + Options.DELIMITER + "Highlight Color"; public static final String HIGHLIGHT_ALT_COLOR_NAME = HIGHLIGHT_GROUP + Options.DELIMITER + "Alternate Highlight Color"; public final static String ARRAY_DISPLAY_OPTIONS = - OPTIONS_GROUP + Options.DELIMITER + "Array Display Options"; + ARRAY_OPTIONS_GROUP + Options.DELIMITER + "Array Display Options"; public final static String ARRAY_DISPLAY_DESCRIPTION = "Adjusts the Array Field display"; private static final int NUM_MODELS = 7; diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/codebrowser/CodeBrowserOptionsTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/codebrowser/CodeBrowserOptionsTest.java index b6515adb21..31506be5f3 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/codebrowser/CodeBrowserOptionsTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/codebrowser/CodeBrowserOptionsTest.java @@ -23,7 +23,10 @@ import java.util.stream.Collectors; import org.junit.*; +import docking.help.Help; +import docking.help.HelpService; import docking.widgets.fieldpanel.field.*; +import generic.test.TestUtils; import ghidra.GhidraOptions; import ghidra.app.cmd.comments.SetCommentCmd; import ghidra.app.cmd.data.CreateDataCmd; @@ -32,6 +35,7 @@ import ghidra.app.cmd.refs.AddMemRefCmd; import ghidra.app.cmd.refs.AddRegisterRefCmd; import ghidra.app.services.ProgramManager; import ghidra.app.util.viewer.field.*; +import ghidra.base.help.GhidraHelpService; import ghidra.framework.cmd.Command; import ghidra.framework.options.Options; import ghidra.framework.options.ToolOptions; @@ -974,35 +978,69 @@ public class CodeBrowserOptionsTest extends AbstractGhidraHeadedIntegrationTest @Test public void testEveryOptionHasHelp() throws Exception { showTool(tool); - + loadProgram(); List missing = new ArrayList<>(); ToolOptions[] toolOptions = tool.getOptions(); + GhidraHelpService.install(); for (ToolOptions options : toolOptions) { - HelpLocation helpLocation = options.getOptionsHelpLocation(); - if (helpLocation != null) { - continue; // this is a top-level help location for all sub-options - } - + HelpLocation optionsHelp = options.getOptionsHelpLocation(); + boolean hasParentHelp = optionsHelp != null; if (CollectionUtils.isOneOf(options.getName(), "Key Bindings", "Listing Display")) { - continue; + continue; // these custom widgets are known to have help } List optionNames = options.getOptionNames(); for (String name : optionNames) { + HelpLocation hl = options.getHelpLocation(name); if (hl == null) { - missing.add(options.getName() + "." + name); + if (!hasParentHelp) { + missing.add("Option missing help: " + options.getName() + "." + name); + } + } + + List nestedHelp = getParentHelpLocations(options, name); + for (HelpLocation help : nestedHelp) { + if (help != null && !isValidHelpLocation(help)) { + missing.add("Bad help location: " + help.toString()); + } + } + + // it has a help location; is it valid? + if (hl != null && !isValidHelpLocation(hl)) { + missing.add(name + "." + name); } } } if (!missing.isEmpty()) { - fail(missing.size() + " Tool Options is missing help\n" + + fail(missing.size() + " Tool Options is missing/invalid help\n" + missing.stream().collect(Collectors.joining("\n"))); } } + private List getParentHelpLocations(ToolOptions options, String name) { + + List list = new LinkedList<>(); + List parts = CollectionUtils.asList(name.split("\\.")); + Collections.reverse(parts); // put lowest-level first + for (String optionName : parts) { + Options parentOption = options.getOptions(optionName); + HelpLocation help = parentOption.getOptionsHelpLocation(); + list.add(help); + } + return list; + } + + private boolean isValidHelpLocation(HelpLocation helpLocation) { + + HelpService help = Help.getHelpService(); + boolean isValid = + (boolean) TestUtils.invokeInstanceMethod("isValidHelpLocation", help, helpLocation); + return isValid; + } + enum DUMMY { // nothing; just a dummy } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/help/HelpManager.java b/Ghidra/Framework/Docking/src/main/java/docking/help/HelpManager.java index 19f9342901..4d48b19f11 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/help/HelpManager.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/help/HelpManager.java @@ -24,7 +24,6 @@ import java.util.*; import java.util.List; import java.util.Map; import java.util.Map.Entry; -import java.util.concurrent.atomic.AtomicReference; import javax.help.*; import javax.help.Map.ID; @@ -497,9 +496,7 @@ public class HelpManager implements HelpService { private Map copyHelpLocations() { // we must copy the help locations, since we are in a background thread and the // locations map is frequently updated by the Swing thread - AtomicReference> ref = new AtomicReference<>(); - SystemUtilities.runSwingNow(() -> ref.set(new HashMap<>(helpLocations))); - return ref.get(); + return Swing.runNow(() -> new HashMap<>(helpLocations)); } //