GP-1014 - Function Call Graph - add option to change background color;

consolidated graph options.
This commit is contained in:
dragonmacher 2021-06-30 16:48:51 -04:00
parent 6962885c3e
commit 9112a84f63
22 changed files with 301 additions and 247 deletions

View file

@ -35,8 +35,6 @@ public interface GhidraOptions {
* Category name for the Browser options that affect the display. * Category name for the Browser options that affect the display.
*/ */
final String CATEGORY_BROWSER_DISPLAY = "Listing Display"; final String CATEGORY_BROWSER_DISPLAY = "Listing Display";
@Deprecated //remove a few versions after 8.0
final String OLD_CATEGORY_BROWSER_DISPLAY = "Browser Display";
/** /**
* Category name for the Browser Navigation Marker options. * Category name for the Browser Navigation Marker options.
@ -101,8 +99,6 @@ public interface GhidraOptions {
* Options name for Browser fields * Options name for Browser fields
*/ */
final String CATEGORY_BROWSER_FIELDS = "Listing Fields"; final String CATEGORY_BROWSER_FIELDS = "Listing Fields";
@Deprecated //remove a few versions after 8.0
final String OLD_CATEGORY_BROWSER_FIELDS = "Browser Fields";
/** /**
* Options title for Mnemonic group. * Options title for Mnemonic group.
@ -125,8 +121,6 @@ public interface GhidraOptions {
* Category name for Browser Popup options * Category name for Browser Popup options
*/ */
final String CATEGORY_BROWSER_POPUPS = "Listing Popups"; final String CATEGORY_BROWSER_POPUPS = "Listing Popups";
@Deprecated //remove a few versions after 8.0
final String OLD_CATEGORY_BROWSER_POPUPS = "Browser Popups";
/** /**
* Category name for Decompiler Popup options * Category name for Decompiler Popup options

View file

@ -131,12 +131,7 @@ public class CodeBrowserPlugin extends Plugin
public CodeBrowserPlugin(PluginTool tool) { public CodeBrowserPlugin(PluginTool tool) {
super(tool); super(tool);
tool.registerOptionsNameChange(GhidraOptions.OLD_CATEGORY_BROWSER_FIELDS,
GhidraOptions.CATEGORY_BROWSER_FIELDS);
tool.registerOptionsNameChange(GhidraOptions.OLD_CATEGORY_BROWSER_DISPLAY,
GhidraOptions.CATEGORY_BROWSER_DISPLAY);
tool.registerOptionsNameChange(GhidraOptions.OLD_CATEGORY_BROWSER_POPUPS,
GhidraOptions.CATEGORY_BROWSER_POPUPS);
ToolOptions displayOptions = tool.getOptions(GhidraOptions.CATEGORY_BROWSER_DISPLAY); ToolOptions displayOptions = tool.getOptions(GhidraOptions.CATEGORY_BROWSER_DISPLAY);
ToolOptions fieldOptions = tool.getOptions(GhidraOptions.CATEGORY_BROWSER_FIELDS); ToolOptions fieldOptions = tool.getOptions(GhidraOptions.CATEGORY_BROWSER_FIELDS);
displayOptions.registerOptionsEditor(new ListingDisplayOptionsEditor(displayOptions)); displayOptions.registerOptionsEditor(new ListingDisplayOptionsEditor(displayOptions));

View file

@ -20,6 +20,7 @@ import java.util.*;
import docking.ActionContext; import docking.ActionContext;
import docking.action.MenuData; import docking.action.MenuData;
import docking.action.ToggleDockingAction; import docking.action.ToggleDockingAction;
import docking.tool.ToolConstants;
import ghidra.app.CorePluginPackage; import ghidra.app.CorePluginPackage;
import ghidra.app.plugin.PluginCategoryNames; import ghidra.app.plugin.PluginCategoryNames;
import ghidra.app.services.GraphDisplayBroker; import ghidra.app.services.GraphDisplayBroker;
@ -90,7 +91,7 @@ public class GraphDisplayBrokerPlugin extends Plugin
private void initializeServices() { private void initializeServices() {
for (GraphDisplayProvider service : graphDisplayProviders) { for (GraphDisplayProvider service : graphDisplayProviders) {
ToolOptions options = tool.getOptions("Graph"); ToolOptions options = tool.getOptions(ToolConstants.GRAPH_OPTIONS);
options.addOptionsChangeListener(this); options.addOptionsChangeListener(this);
service.initialize(tool, options); service.initialize(tool, options);
} }
@ -148,7 +149,7 @@ public class GraphDisplayBrokerPlugin extends Plugin
@Override @Override
public GraphDisplay getDefaultGraphDisplay(boolean reuseGraph, Map<String, String> properties, public GraphDisplay getDefaultGraphDisplay(boolean reuseGraph, Map<String, String> properties,
TaskMonitor monitor) throws GraphException { TaskMonitor monitor) throws GraphException {
if (defaultGraphDisplayProvider != null) { if (defaultGraphDisplayProvider != null) {
return defaultGraphDisplayProvider.getGraphDisplay(reuseGraph, properties, monitor); return defaultGraphDisplayProvider.getGraphDisplay(reuseGraph, properties, monitor);
} }

View file

@ -665,7 +665,7 @@ class FGActionManager {
@Override @Override
public void actionPerformed(ActionContext context) { public void actionPerformed(ActionContext context) {
OptionsService service = tool.getService(OptionsService.class); OptionsService service = tool.getService(OptionsService.class);
service.showOptionsDialog(FunctionGraphPlugin.PLUGIN_OPTIONS_NAME, service.showOptionsDialog(FunctionGraphPlugin.OPTIONS_NAME_PATH,
"Function Graph"); "Function Graph");
} }

View file

@ -21,6 +21,7 @@ import javax.swing.ImageIcon;
import org.jdom.Element; import org.jdom.Element;
import docking.tool.ToolConstants;
import ghidra.GhidraOptions; import ghidra.GhidraOptions;
import ghidra.app.CorePluginPackage; import ghidra.app.CorePluginPackage;
import ghidra.app.events.*; import ghidra.app.events.*;
@ -57,7 +58,8 @@ import resources.ResourceManager;
//@formatter:on //@formatter:on
public class FunctionGraphPlugin extends ProgramPlugin implements OptionsChangeListener { public class FunctionGraphPlugin extends ProgramPlugin implements OptionsChangeListener {
static final String FUNCTION_GRAPH_NAME = "Function Graph"; static final String FUNCTION_GRAPH_NAME = "Function Graph";
static final String PLUGIN_OPTIONS_NAME = FUNCTION_GRAPH_NAME; static final String OPTIONS_NAME_PATH =
ToolConstants.GRAPH_OPTIONS + Options.DELIMITER + FUNCTION_GRAPH_NAME;
static final ImageIcon ICON = ResourceManager.loadImage("images/function_graph.png"); static final ImageIcon ICON = ResourceManager.loadImage("images/function_graph.png");
@ -147,14 +149,20 @@ public class FunctionGraphPlugin extends ProgramPlugin implements OptionsChangeL
} }
private void initializeOptions() { private void initializeOptions() {
ToolOptions options = tool.getOptions(PLUGIN_OPTIONS_NAME); ToolOptions options = tool.getOptions(ToolConstants.GRAPH_OPTIONS);
options.addOptionsChangeListener(this); options.addOptionsChangeListener(this);
functionGraphOptions.registerOptions(options);
functionGraphOptions.loadOptions(options); // Graph -> Function Graph
Options fgOptions = options.getOptions(FUNCTION_GRAPH_NAME);
functionGraphOptions.registerOptions(fgOptions);
functionGraphOptions.loadOptions(fgOptions);
for (FGLayoutProvider layoutProvider : layoutProviders) { for (FGLayoutProvider layoutProvider : layoutProviders) {
// Graph -> Function Graph -> Layout Name
String layoutName = layoutProvider.getLayoutName(); String layoutName = layoutProvider.getLayoutName();
Options layoutToolOptions = options.getOptions(layoutName); Options layoutToolOptions = fgOptions.getOptions(layoutName);
FGLayoutOptions layoutOptions = layoutProvider.createLayoutOptions(layoutToolOptions); FGLayoutOptions layoutOptions = layoutProvider.createLayoutOptions(layoutToolOptions);
if (layoutOptions == null) { if (layoutOptions == null) {
continue; // many layouts do not have options continue; // many layouts do not have options
@ -170,7 +178,9 @@ public class FunctionGraphPlugin extends ProgramPlugin implements OptionsChangeL
public void optionsChanged(ToolOptions options, String optionName, Object oldValue, public void optionsChanged(ToolOptions options, String optionName, Object oldValue,
Object newValue) { Object newValue) {
functionGraphOptions.loadOptions(options); // Graph -> Function Graph
Options fgOptions = options.getOptions(FUNCTION_GRAPH_NAME);
functionGraphOptions.loadOptions(fgOptions);
connectedProvider.optionsChanged(); connectedProvider.optionsChanged();

View file

@ -220,8 +220,8 @@ public class FGComponent extends GraphComponent<FGVertex, FGEdge, FunctionGraph>
edgeLabelRenderer.setRotateEdgeLabels(false); edgeLabelRenderer.setRotateEdgeLabels(false);
renderContext.setEdgeLabelRenderer(edgeLabelRenderer); renderContext.setEdgeLabelRenderer(edgeLabelRenderer);
viewer.setGraphOptions(options); viewer.setGraphOptions(vgOptions);
Color bgColor = options.getGraphBackgroundColor(); Color bgColor = vgOptions.getGraphBackgroundColor();
if (bgColor.equals(VisualGraphOptions.DEFAULT_GRAPH_BACKGROUND_COLOR)) { if (bgColor.equals(VisualGraphOptions.DEFAULT_GRAPH_BACKGROUND_COLOR)) {
// Give user notice when seeing the graph for a non-function (such as an undefined // Give user notice when seeing the graph for a non-function (such as an undefined
@ -259,7 +259,7 @@ public class FGComponent extends GraphComponent<FGVertex, FGEdge, FunctionGraph>
renderContext.setVertexFillPaintTransformer(new FGVertexPickableBackgroundPaintTransformer( renderContext.setVertexFillPaintTransformer(new FGVertexPickableBackgroundPaintTransformer(
pickedVertexState, Color.YELLOW, START_COLOR, END_COLOR)); pickedVertexState, Color.YELLOW, START_COLOR, END_COLOR));
viewer.setGraphOptions(options); viewer.setGraphOptions(vgOptions);
return viewer; return viewer;
} }
@ -287,7 +287,7 @@ public class FGComponent extends GraphComponent<FGVertex, FGEdge, FunctionGraph>
//================================================================================================== //==================================================================================================
public FunctionGraphOptions getFucntionGraphOptions() { public FunctionGraphOptions getFucntionGraphOptions() {
return (FunctionGraphOptions) options; return (FunctionGraphOptions) vgOptions;
} }
public void ensureCursorVisible(FGVertex vertex) { public void ensureCursorVisible(FGVertex vertex) {

View file

@ -22,7 +22,8 @@ import java.util.Map.Entry;
import ghidra.app.plugin.core.functiongraph.FunctionGraphPlugin; import ghidra.app.plugin.core.functiongraph.FunctionGraphPlugin;
import ghidra.app.plugin.core.functiongraph.graph.layout.FGLayoutOptions; import ghidra.app.plugin.core.functiongraph.graph.layout.FGLayoutOptions;
import ghidra.framework.options.Options; import ghidra.framework.options.Options;
import ghidra.graph.viewer.options.*; import ghidra.graph.viewer.options.RelayoutOption;
import ghidra.graph.viewer.options.VisualGraphOptions;
import ghidra.program.model.symbol.FlowType; import ghidra.program.model.symbol.FlowType;
import ghidra.util.HelpLocation; import ghidra.util.HelpLocation;
@ -162,7 +163,7 @@ public class FunctionGraphOptions extends VisualGraphOptions {
public void registerOptions(Options options) { public void registerOptions(Options options) {
HelpLocation help = new HelpLocation(OWNER, "Options"); HelpLocation help = new HelpLocation(OWNER, "Options");
options.setOptionsHelpLocation(help); super.registerOptions(options, help);
options.registerOption(RELAYOUT_OPTIONS_KEY, relayoutOption, help, options.registerOption(RELAYOUT_OPTIONS_KEY, relayoutOption, help,
RELAYOUT_OPTIONS_DESCRIPTION); RELAYOUT_OPTIONS_DESCRIPTION);
@ -170,24 +171,9 @@ public class FunctionGraphOptions extends VisualGraphOptions {
options.registerOption(NAVIGATION_HISTORY_KEY, navigationHistoryChoice, help, options.registerOption(NAVIGATION_HISTORY_KEY, navigationHistoryChoice, help,
NAVIGATION_HISTORY_DESCRIPTION); NAVIGATION_HISTORY_DESCRIPTION);
options.registerOption(SHOW_ANIMATION_OPTIONS_KEY, useAnimation(), help,
SHOW_ANIMATION_DESCRIPTION);
options.registerOption(USE_MOUSE_RELATIVE_ZOOM_KEY, useMouseRelativeZoom(), help,
USE_MOUSE_RELATIVE_ZOOM_DESCRIPTION);
options.registerOption(USE_CONDENSED_LAYOUT_KEY, useCondensedLayout(), options.registerOption(USE_CONDENSED_LAYOUT_KEY, useCondensedLayout(),
new HelpLocation(OWNER, "Layout_Compressing"), USE_CONDENSED_LAYOUT_DESCRIPTION); new HelpLocation(OWNER, "Layout_Compressing"), USE_CONDENSED_LAYOUT_DESCRIPTION);
options.registerOption(VIEW_RESTORE_OPTIONS_KEY, ViewRestoreOption.START_FULLY_ZOOMED_OUT,
help, VIEW_RESTORE_OPTIONS_DESCRIPTION);
options.registerOption(SCROLL_WHEEL_PANS_KEY, getScrollWheelPans(), help,
SCROLL_WHEEL_PANS_DESCRIPTION);
options.registerOption(GRAPH_BACKGROUND_COLOR_KEY, DEFAULT_GRAPH_BACKGROUND_COLOR, help,
GRAPH_BACKGROUND_COLOR_DESCRPTION);
options.registerOption(DEFAULT_VERTEX_BACKGROUND_COLOR_KEY, DEFAULT_VERTEX_BACKGROUND_COLOR, options.registerOption(DEFAULT_VERTEX_BACKGROUND_COLOR_KEY, DEFAULT_VERTEX_BACKGROUND_COLOR,
help, DEFAULT_VERTEX_BACKGROUND_COLOR_DESCRPTION); help, DEFAULT_VERTEX_BACKGROUND_COLOR_DESCRPTION);
@ -222,7 +208,11 @@ public class FunctionGraphOptions extends VisualGraphOptions {
} }
@Override
public void loadOptions(Options options) { public void loadOptions(Options options) {
super.loadOptions(options);
conditionalJumpEdgeColor = conditionalJumpEdgeColor =
options.getColor(EDGE_COLOR_CONDITIONAL_JUMP_KEY, conditionalJumpEdgeColor); options.getColor(EDGE_COLOR_CONDITIONAL_JUMP_KEY, conditionalJumpEdgeColor);
@ -245,23 +235,8 @@ public class FunctionGraphOptions extends VisualGraphOptions {
navigationHistoryChoice = navigationHistoryChoice =
options.getEnum(NAVIGATION_HISTORY_KEY, NavigationHistoryChoices.VERTEX_CHANGES); options.getEnum(NAVIGATION_HISTORY_KEY, NavigationHistoryChoices.VERTEX_CHANGES);
useAnimation = options.getBoolean(SHOW_ANIMATION_OPTIONS_KEY, useAnimation);
useMouseRelativeZoom =
options.getBoolean(USE_MOUSE_RELATIVE_ZOOM_KEY, useMouseRelativeZoom);
useCondensedLayout = options.getBoolean(USE_CONDENSED_LAYOUT_KEY, useCondensedLayout);
useFullSizeTooltip = options.getBoolean(USE_FULL_SIZE_TOOLTIP_KEY, useFullSizeTooltip); useFullSizeTooltip = options.getBoolean(USE_FULL_SIZE_TOOLTIP_KEY, useFullSizeTooltip);
viewRestoreOption =
options.getEnum(VIEW_RESTORE_OPTIONS_KEY, ViewRestoreOption.START_FULLY_ZOOMED_OUT);
scrollWheelPans = options.getBoolean(SCROLL_WHEEL_PANS_KEY, scrollWheelPans);
graphBackgroundColor =
options.getColor(GRAPH_BACKGROUND_COLOR_KEY, DEFAULT_GRAPH_BACKGROUND_COLOR);
defaultVertexBackgroundColor = defaultVertexBackgroundColor =
options.getColor(DEFAULT_VERTEX_BACKGROUND_COLOR_KEY, DEFAULT_VERTEX_BACKGROUND_COLOR); options.getColor(DEFAULT_VERTEX_BACKGROUND_COLOR_KEY, DEFAULT_VERTEX_BACKGROUND_COLOR);

View file

@ -232,6 +232,15 @@
</P> </P>
</BLOCKQUOTE> </BLOCKQUOTE>
<H2><A name="Options"></A>Options</H2>
<BLOCKQUOTE>
<P>The Function Call Graph options are currently a subset of the
<A href="help/topics/FunctionGraphPlugin/Function_Graph.html#Options">
Function Graph's Options</A>.
</P>
</BLOCKQUOTE>
</BLOCKQUOTE> </BLOCKQUOTE>
@ -240,8 +249,7 @@
<P class="relatedtopic">Related Topics:</P> <P class="relatedtopic">Related Topics:</P>
<UL> <UL>
<!-- TODO update this when we introduce the generic graphing help --> <LI><A href="help/topics/Graph/GraphIntro.html">Graphs</A></LI>
<LI>Graphs</LI>
</UL><BR> </UL><BR>
<BR> <BR>
</BODY> </BODY>

View file

@ -25,11 +25,17 @@ import ghidra.graph.viewer.options.VisualGraphOptions;
*/ */
public class FcgView extends VisualGraphView<FcgVertex, FcgEdge, FunctionCallGraph> { public class FcgView extends VisualGraphView<FcgVertex, FcgEdge, FunctionCallGraph> {
private VisualGraphOptions options;
public FcgView(VisualGraphOptions options) {
this.options = options;
}
@Override @Override
protected void installGraphViewer() { protected void installGraphViewer() {
FcgComponent component = createGraphComponent(); FcgComponent component = createGraphComponent();
component.setGraphOptions(new VisualGraphOptions()); component.setGraphOptions(options);
setGraphComponent(component); setGraphComponent(component);
} }

View file

@ -15,8 +15,7 @@
*/ */
package functioncalls.plugin; package functioncalls.plugin;
import static functioncalls.graph.FcgDirection.IN; import static functioncalls.graph.FcgDirection.*;
import static functioncalls.graph.FcgDirection.OUT;
import java.awt.*; import java.awt.*;
import java.awt.event.MouseEvent; import java.awt.event.MouseEvent;
@ -133,6 +132,10 @@ public class FcgProvider
installGraph(); installGraph();
} }
void optionsChanged() {
view.optionsChanged();
}
void locationChanged(ProgramLocation loc) { void locationChanged(ProgramLocation loc) {
if (!navigateIncomingToggleAction.isSelected()) { if (!navigateIncomingToggleAction.isSelected()) {
return; return;
@ -277,7 +280,7 @@ public class FcgProvider
private void buildComponent() { private void buildComponent() {
view = new FcgView(); view = new FcgView(plugin.getOptions());
view.setVertexClickListener((v, info) -> { view.setVertexClickListener((v, info) -> {
@ -519,7 +522,7 @@ public class FcgProvider
addLocalAction(resetGraphAction); addLocalAction(resetGraphAction);
MultiStateDockingAction<LayoutProvider<FcgVertex, FcgEdge, FunctionCallGraph>> layoutAction = MultiStateDockingAction<LayoutProvider<FcgVertex, FcgEdge, FunctionCallGraph>> layoutAction =
new MultiStateDockingAction<LayoutProvider<FcgVertex, FcgEdge, FunctionCallGraph>>( new MultiStateDockingAction<>(
RELAYOUT_GRAPH_ACTION_NAME, plugin.getName()) { RELAYOUT_GRAPH_ACTION_NAME, plugin.getName()) {
@Override @Override

View file

@ -17,18 +17,21 @@ package functioncalls.plugin;
import docking.ActionContext; import docking.ActionContext;
import docking.action.DockingAction; import docking.action.DockingAction;
import docking.tool.ToolConstants;
import ghidra.app.CorePluginPackage; import ghidra.app.CorePluginPackage;
import ghidra.app.plugin.PluginCategoryNames; import ghidra.app.plugin.PluginCategoryNames;
import ghidra.app.plugin.ProgramPlugin; import ghidra.app.plugin.ProgramPlugin;
import ghidra.app.services.GoToService; import ghidra.app.services.GoToService;
import ghidra.framework.options.SaveState; import ghidra.framework.options.*;
import ghidra.framework.plugintool.PluginInfo; import ghidra.framework.plugintool.PluginInfo;
import ghidra.framework.plugintool.PluginTool; import ghidra.framework.plugintool.PluginTool;
import ghidra.framework.plugintool.util.PluginStatus; import ghidra.framework.plugintool.util.PluginStatus;
import ghidra.graph.viewer.options.VisualGraphOptions;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.util.ProgramLocation; import ghidra.program.util.ProgramLocation;
import ghidra.util.HelpLocation; import ghidra.util.HelpLocation;
import ghidra.util.SystemUtilities; import ghidra.util.SystemUtilities;
import ghidra.util.bean.opteditor.OptionsVetoException;
import ghidra.util.task.SwingUpdateManager; import ghidra.util.task.SwingUpdateManager;
/** /**
@ -43,7 +46,7 @@ import ghidra.util.task.SwingUpdateManager;
description = "Displays a graph of incoming and outgoing calls for a given function." description = "Displays a graph of incoming and outgoing calls for a given function."
) )
//@formatter:on //@formatter:on
public class FunctionCallGraphPlugin extends ProgramPlugin { public class FunctionCallGraphPlugin extends ProgramPlugin implements OptionsChangeListener {
/*package*/ static final String NAME = "Function Call Graph"; /*package*/ static final String NAME = "Function Call Graph";
/*package*/ static final String SHOW_PROVIDER_ACTION_NAME = "Display Function Call Graph"; /*package*/ static final String SHOW_PROVIDER_ACTION_NAME = "Display Function Call Graph";
@ -52,6 +55,7 @@ public class FunctionCallGraphPlugin extends ProgramPlugin {
FunctionCallGraphPlugin.class.getSimpleName()); FunctionCallGraphPlugin.class.getSimpleName());
private FcgProvider provider; private FcgProvider provider;
private VisualGraphOptions vgOptions = new VisualGraphOptions();
// enough time for users to click around without the graph starting its work // enough time for users to click around without the graph starting its work
private static final int MIN_UPDATE_DELAY = 750; private static final int MIN_UPDATE_DELAY = 750;
@ -65,8 +69,32 @@ public class FunctionCallGraphPlugin extends ProgramPlugin {
@Override @Override
protected void init() { protected void init() {
provider = new FcgProvider(tool, this); provider = new FcgProvider(tool, this);
createActions(); createActions();
initializeOptions();
}
private void initializeOptions() {
ToolOptions options = tool.getOptions(ToolConstants.GRAPH_OPTIONS);
options.addOptionsChangeListener(this);
HelpLocation help = new HelpLocation(getName(), "Options");
Options callGraphOptions = options.getOptions(NAME);
vgOptions.registerOptions(callGraphOptions, help);
vgOptions.loadOptions(callGraphOptions);
provider.optionsChanged();
}
@Override
public void optionsChanged(ToolOptions options, String optionName, Object oldValue,
Object newValue) throws OptionsVetoException {
Options callGraphOptions = options.getOptions(NAME);
vgOptions.loadOptions(callGraphOptions);
provider.optionsChanged();
} }
@Override @Override
@ -142,4 +170,8 @@ public class FunctionCallGraphPlugin extends ProgramPlugin {
ProgramLocation getCurrentLocation() { ProgramLocation getCurrentLocation() {
return currentLocation; return currentLocation;
} }
VisualGraphOptions getOptions() {
return vgOptions;
}
} }

View file

@ -23,6 +23,7 @@ import docking.action.DockingAction;
import docking.action.ToggleDockingAction; import docking.action.ToggleDockingAction;
import docking.action.builder.ActionBuilder; import docking.action.builder.ActionBuilder;
import docking.action.builder.ToggleActionBuilder; import docking.action.builder.ToggleActionBuilder;
import docking.tool.ToolConstants;
import ghidra.app.CorePluginPackage; import ghidra.app.CorePluginPackage;
import ghidra.app.events.ProgramLocationPluginEvent; import ghidra.app.events.ProgramLocationPluginEvent;
import ghidra.app.events.ProgramSelectionPluginEvent; import ghidra.app.events.ProgramSelectionPluginEvent;
@ -69,11 +70,18 @@ import ghidra.util.task.TaskLauncher;
//@formatter:on //@formatter:on
public class ProgramGraphPlugin extends ProgramPlugin public class ProgramGraphPlugin extends ProgramPlugin
implements OptionsChangeListener, BlockModelServiceListener, GraphDisplayBrokerListener { implements OptionsChangeListener, BlockModelServiceListener, GraphDisplayBrokerListener {
private static final String MAX_CODE_LINES_DISPLAYED = "Max Code Lines Displayed";
private static final String REUSE_GRAPH = "Reuse Graph"; private static final String PLUGIN_NAME = "Program Graph";
private static final String GRAPH_ENTRY_POINT_NEXUS = "Graph Entry Point Nexus";
private static final String FORCE_LOCATION_DISPLAY_OPTION = "Force Location Visible on Graph"; private static final String OPTIONS_PREFIX = PLUGIN_NAME + Options.DELIMITER;
private static final String MAX_DEPTH_OPTION = "Max Reference Depth"; private static final String MAX_CODE_LINES_DISPLAYED =
OPTIONS_PREFIX + "Max Code Lines Displayed";
private static final String REUSE_GRAPH = OPTIONS_PREFIX + "Reuse Graph";
private static final String GRAPH_ENTRY_POINT_NEXUS =
OPTIONS_PREFIX + "Graph Entry Point Nexus";
private static final String FORCE_LOCATION_DISPLAY_OPTION =
OPTIONS_PREFIX + "Force Location Visible on Graph";
private static final String MAX_DEPTH_OPTION = OPTIONS_PREFIX + "Max Reference Depth";
public static final String MENU_GRAPH = "&Graph"; public static final String MENU_GRAPH = "&Graph";
private BlockModelService blockModelService; private BlockModelService blockModelService;
@ -102,23 +110,23 @@ public class ProgramGraphPlugin extends ProgramPlugin
private void intializeOptions() { private void intializeOptions() {
HelpLocation help = new HelpLocation(getName(), "Graph_Option"); HelpLocation help = new HelpLocation(getName(), "Graph_Option");
ToolOptions options = tool.getOptions("Graph"); ToolOptions options = tool.getOptions(ToolConstants.GRAPH_OPTIONS);
options.registerOption(MAX_CODE_LINES_DISPLAYED, codeLimitPerBlock, help, options.registerOption(MAX_CODE_LINES_DISPLAYED, codeLimitPerBlock, help,
"Specifies the maximum number of instructions to display in each graph " + "Specifies the maximum number of instructions to display in each graph " +
"node in a Code Flow Graph."); "node in a Code Flow Graph.");
options.registerOption(REUSE_GRAPH, false, help, options.registerOption(REUSE_GRAPH, false, help,
"Determines whether the graph will reuse the active graph window when displaying graphs."); "Determines whether the graph will reuse the active graph window when displaying " +
"graphs.");
options.registerOption(GRAPH_ENTRY_POINT_NEXUS, false, help, options.registerOption(GRAPH_ENTRY_POINT_NEXUS, false, help,
"Add a dummy node at the root of the graph and adds dummy edges to each node that has " + "Add a dummy node at the root of the graph and adds dummy edges to each node that " +
"no incoming edges."); "has no incoming edges.");
options.registerOption(FORCE_LOCATION_DISPLAY_OPTION, false, help, options.registerOption(FORCE_LOCATION_DISPLAY_OPTION, false, help,
"Specifies whether or not " + "Specifies whether or not graph displays should force the visible graph to pan " +
"graph displays should force the visible graph to pan and/or scale to ensure that focused " + "and/or scale to ensure that focused locations are visible.");
"locations are visible.");
options.registerOption(MAX_DEPTH_OPTION, 1, help, options.registerOption(MAX_DEPTH_OPTION, 1, help,
"Specifies max depth of data references to graph (0 for no limit)"); "Specifies max depth of data references to graph (0 for no limit)");

View file

@ -99,10 +99,15 @@ public interface ToolConstants extends DockingToolConstants {
public static final String SHARED_OWNER = "Shared"; public static final String SHARED_OWNER = "Shared";
/** /**
* Name of options for a tool * Tool options name
*/ */
public static final String TOOL_OPTIONS = "Tool"; public static final String TOOL_OPTIONS = "Tool";
/**
* Graph options name
*/
public static final String GRAPH_OPTIONS = "Graph";
/** /**
* Name of the help topic for "About" domain objects and Ghidra * Name of the help topic for "About" domain objects and Ghidra
*/ */

View file

@ -56,6 +56,7 @@ public interface Options {
/** /**
* Get the property editor for the option with the given name. Note: This method must be called * Get the property editor for the option with the given name. Note: This method must be called
* from the swing thread. * from the swing thread.
* @param optionName the option name
* @return either the PropertyEditor that was registered for this option or a default editor * @return either the PropertyEditor that was registered for this option or a default editor
* for the property type if one can be found; otherwise null. * for the property type if one can be found; otherwise null.
* @throws IllegalStateException if not called from the swing thread. * @throws IllegalStateException if not called from the swing thread.
@ -65,6 +66,7 @@ public interface Options {
/** /**
* Get the property editor that was registered for the specific option with the given name. Unlike * Get the property editor that was registered for the specific option with the given name. Unlike
* the getPropertyEditor() method, this method does not have to be called from the swing thread * the getPropertyEditor() method, this method does not have to be called from the swing thread
* @param optionName the option name
* @return the PropertyEditor that was registered for this option. * @return the PropertyEditor that was registered for this option.
*/ */
@ -163,6 +165,8 @@ public interface Options {
/** /**
* Put the object value. If the option exists, the type must match the type of the existing * Put the object value. If the option exists, the type must match the type of the existing
* object. * object.
* @param optionName the option name
* @param obj the option value
* @throws IllegalStateException if the object does not match the existing type of the option. * @throws IllegalStateException if the object does not match the existing type of the option.
* @throws IllegalArgumentException if the object is null or not a supported type. * @throws IllegalArgumentException if the object is null or not a supported type.
*/ */
@ -352,21 +356,21 @@ public interface Options {
/** /**
* Sets the Custom option value for the option. * Sets the Custom option value for the option.
* @param optionName name of the option * @param optionName name of the option
* @param value * @param value the value
*/ */
public abstract void setCustomOption(String optionName, CustomOption value); public abstract void setCustomOption(String optionName, CustomOption value);
/** /**
* Sets the byte[] value for the given option name. * Sets the byte[] value for the given option name.
* @param optionName the name of the option on which to save bytes. * @param optionName the name of the option on which to save bytes.
* @param value * @param value the value
*/ */
public abstract void setByteArray(String optionName, byte[] value); public abstract void setByteArray(String optionName, byte[] value);
/** /**
* Sets the File value for the option. * Sets the File value for the option.
* @param optionName name of the option * @param optionName name of the option
* @param value * @param value the value
*/ */
public abstract void setFile(String optionName, File value); public abstract void setFile(String optionName, File value);
@ -436,6 +440,7 @@ public interface Options {
/** /**
* Return true if a option exists with the given name. * Return true if a option exists with the given name.
* @param optionName option name * @param optionName option name
* @return true if there exists an option with the given name
*/ */
public abstract boolean contains(String optionName); public abstract boolean contains(String optionName);
@ -449,6 +454,8 @@ public interface Options {
/** /**
* Returns true if the specified option has been registered. Only registered names * Returns true if the specified option has been registered. Only registered names
* are saved. * are saved.
* @param optionName the option name
* @return true if registered
*/ */
public abstract boolean isRegistered(String optionName); public abstract boolean isRegistered(String optionName);
@ -476,14 +483,20 @@ public interface Options {
/** /**
* Returns a Options object that is a sub-options of this options. * Returns a Options object that is a sub-options of this options.
* @param path the path for the sub-options object. *
* @return a Options object that is a sub-options of this options. * <p>Note: the option path can have {@link Options#DELIMITER} characters which will be
* used to create a hierarchy with each element in the path resulting in sub-option of the
* previous path element.
*
* @param path the path for the sub-options object
* @return an Options object that is a sub-options of this options
*/ */
public Options getOptions(String path); public Options getOptions(String path);
/** /**
* Create an alias in this options for an existing option in some other options object. * Create an alias in this options for an existing option in some other options object.
* @param aliasName the name within this options object that will acutally refer to some other options object. * @param aliasName the name within this options object that will actually refer to some other
* options object.
* @param options the options object that has the actual option. * @param options the options object that has the actual option.
* @param optionsName the name within the given options object of the actual option. * @param optionsName the name within the given options object of the actual option.
*/ */

View file

@ -32,13 +32,17 @@ import ghidra.util.exception.AssertException;
/** /**
* Class to manage a set of option name/value pairs for a category. * Class to manage a set of option name/value pairs for a category.
* The values may be primitives or *
* WrappedObjects that are containers for its primitive components. * <p>The values may be primitives or {@link WrappedOption}s that are containers for primitive
* The option may be associated with a particular group. * components.
* <p> The name/value pair has a owner so that the option name *
* can be removed from the Options object when it is no longer being used. * <p>The name/value pair has an owner so that the option name can be removed from the Options
* <p>NOTE: Property Names can have DELIMITER characters to create a hierarchy. * object when it is no longer being used.
* The Options Dialog shows the hierarchy in tree format. *
* <p>Note: Property Names can have {@link Options#DELIMITER} characters to create a hierarchy.
* So too can sub-options accessed via {@link #getOptions(String)}.
*
* <p>The Options Dialog shows the delimited hierarchy in tree format.
*/ */
public class ToolOptions extends AbstractOptions { public class ToolOptions extends AbstractOptions {
private static final String CLASS_ATTRIBUTE = "CLASS"; private static final String CLASS_ATTRIBUTE = "CLASS";

View file

@ -124,7 +124,7 @@ public class GraphComponent<V extends VisualVertex, E extends VisualEdge<V>, G e
// a cache to prevent unnecessary layout calculations // a cache to prevent unnecessary layout calculations
private Dimension lastSize; private Dimension lastSize;
protected VisualGraphOptions options = new VisualGraphOptions(); protected VisualGraphOptions vgOptions = new VisualGraphOptions();
public GraphComponent(G graph) { public GraphComponent(G graph) {
@ -208,7 +208,7 @@ public class GraphComponent<V extends VisualVertex, E extends VisualEdge<V>, G e
renderContext.setVertexFillPaintTransformer( renderContext.setVertexFillPaintTransformer(
new PickableVertexPaintTransformer<>(pickedVertexState, Color.WHITE, Color.YELLOW)); new PickableVertexPaintTransformer<>(pickedVertexState, Color.WHITE, Color.YELLOW));
viewer.setGraphOptions(options); viewer.setGraphOptions(vgOptions);
return viewer; return viewer;
} }
@ -295,7 +295,7 @@ public class GraphComponent<V extends VisualVertex, E extends VisualEdge<V>, G e
SatelliteGraphViewer<V, E> viewer = createSatelliteGraphViewer(masterViewer, viewerSize); SatelliteGraphViewer<V, E> viewer = createSatelliteGraphViewer(masterViewer, viewerSize);
viewer.setGraphOptions(options); viewer.setGraphOptions(vgOptions);
viewer.setMinimumSize(viewerSize); viewer.setMinimumSize(viewerSize);
viewer.setMaximumSize(viewerSize); viewer.setMaximumSize(viewerSize);
@ -434,7 +434,7 @@ public class GraphComponent<V extends VisualVertex, E extends VisualEdge<V>, G e
/* /*
TODO fix when the help module is created TODO fix when the Generic Visual Graph help module is created
HelpService helpService = DockingWindowManager.getHelpService(); HelpService helpService = DockingWindowManager.getHelpService();
helpService.registerHelp(button, helpService.registerHelp(button,
@ -524,7 +524,7 @@ public class GraphComponent<V extends VisualVertex, E extends VisualEdge<V>, G e
} }
public void setGraphOptions(VisualGraphOptions options) { public void setGraphOptions(VisualGraphOptions options) {
this.options = options; this.vgOptions = options;
// the viewers may be null if called during initialization // the viewers may be null if called during initialization
if (primaryViewer != null) { if (primaryViewer != null) {
@ -536,6 +536,10 @@ public class GraphComponent<V extends VisualVertex, E extends VisualEdge<V>, G e
} }
} }
public VisualGraphOptions getGraphOptions() {
return vgOptions;
}
public boolean isUninitialized() { public boolean isUninitialized() {
return isUninitialized; return isUninitialized;
} }
@ -614,7 +618,7 @@ public class GraphComponent<V extends VisualVertex, E extends VisualEdge<V>, G e
// //
// Default Zoom - Zoomed-out or Zoomed-in? // Default Zoom - Zoomed-out or Zoomed-in?
// //
ViewRestoreOption viewOption = options.getViewRestoreOption(); ViewRestoreOption viewOption = vgOptions.getViewRestoreOption();
if (viewOption == ViewRestoreOption.START_FULLY_ZOOMED_IN) { if (viewOption == ViewRestoreOption.START_FULLY_ZOOMED_IN) {
zoomInCompletely(getInitialVertex()); zoomInCompletely(getInitialVertex());
} }

View file

@ -66,7 +66,7 @@ public class GraphViewer<V extends VisualVertex, E extends VisualEdge<V>>
private PopupRegulator<V, E> popupRegulator; private PopupRegulator<V, E> popupRegulator;
private VertexTooltipProvider<V, E> vertexTooltipProvider = new DummyTooltipProvider(); private VertexTooltipProvider<V, E> vertexTooltipProvider = new DummyTooltipProvider();
protected VisualGraphOptions options; protected VisualGraphOptions vgOptions;
private VisualGraphViewUpdater<V, E> viewUpdater; private VisualGraphViewUpdater<V, E> viewUpdater;
private VisualGraphPathHighlighter<V, E> pathHighlighter; private VisualGraphPathHighlighter<V, E> pathHighlighter;
@ -157,16 +157,16 @@ public class GraphViewer<V extends VisualVertex, E extends VisualEdge<V>>
} }
public void setGraphOptions(VisualGraphOptions options) { public void setGraphOptions(VisualGraphOptions options) {
this.options = options; this.vgOptions = options;
optionsChanged(); optionsChanged();
} }
public void optionsChanged() { public void optionsChanged() {
setBackground(options.getGraphBackgroundColor()); setBackground(vgOptions.getGraphBackgroundColor());
} }
public VisualGraphOptions getOptions() { public VisualGraphOptions getOptions() {
return options; return vgOptions;
} }
public void setVertexHoverPathHighlightMode(PathHighlightMode hoverMode) { public void setVertexHoverPathHighlightMode(PathHighlightMode hoverMode) {
@ -221,7 +221,7 @@ public class GraphViewer<V extends VisualVertex, E extends VisualEdge<V>>
* @return true if using mouse-relative zoom * @return true if using mouse-relative zoom
*/ */
public boolean useMouseRelativeZoom() { public boolean useMouseRelativeZoom() {
return options.useMouseRelativeZoom(); return vgOptions.useMouseRelativeZoom();
} }
/** /**

View file

@ -18,6 +18,8 @@ package ghidra.graph.viewer.options;
import java.awt.Color; import java.awt.Color;
import docking.DockingUtils; import docking.DockingUtils;
import ghidra.framework.options.Options;
import ghidra.util.HelpLocation;
public class VisualGraphOptions { public class VisualGraphOptions {
@ -96,4 +98,41 @@ public class VisualGraphOptions {
return useCondensedLayout; return useCondensedLayout;
} }
public void registerOptions(Options options, HelpLocation help) {
options.setOptionsHelpLocation(help);
options.registerOption(SHOW_ANIMATION_OPTIONS_KEY, useAnimation(), help,
SHOW_ANIMATION_DESCRIPTION);
options.registerOption(USE_MOUSE_RELATIVE_ZOOM_KEY, useMouseRelativeZoom(), help,
USE_MOUSE_RELATIVE_ZOOM_DESCRIPTION);
options.registerOption(VIEW_RESTORE_OPTIONS_KEY, ViewRestoreOption.START_FULLY_ZOOMED_OUT,
help, VIEW_RESTORE_OPTIONS_DESCRIPTION);
options.registerOption(SCROLL_WHEEL_PANS_KEY, getScrollWheelPans(), help,
SCROLL_WHEEL_PANS_DESCRIPTION);
options.registerOption(GRAPH_BACKGROUND_COLOR_KEY, DEFAULT_GRAPH_BACKGROUND_COLOR, help,
GRAPH_BACKGROUND_COLOR_DESCRPTION);
}
public void loadOptions(Options options) {
useAnimation = options.getBoolean(SHOW_ANIMATION_OPTIONS_KEY, useAnimation);
useMouseRelativeZoom =
options.getBoolean(USE_MOUSE_RELATIVE_ZOOM_KEY, useMouseRelativeZoom);
useCondensedLayout = options.getBoolean(USE_CONDENSED_LAYOUT_KEY, useCondensedLayout);
viewRestoreOption =
options.getEnum(VIEW_RESTORE_OPTIONS_KEY, ViewRestoreOption.START_FULLY_ZOOMED_OUT);
scrollWheelPans = options.getBoolean(SCROLL_WHEEL_PANS_KEY, scrollWheelPans);
graphBackgroundColor =
options.getColor(GRAPH_BACKGROUND_COLOR_KEY, DEFAULT_GRAPH_BACKGROUND_COLOR);
}
} }

View file

@ -710,11 +710,6 @@ public abstract class PluginTool extends AbstractDockingTool {
new TaskLauncher(task, winMgr.getActiveWindow()); new TaskLauncher(task, winMgr.getActiveWindow());
} }
/**
* Get the options for the given category name; if no options exist with
* the given name, then one is created.
*/
@Override @Override
public ToolOptions getOptions(String categoryName) { public ToolOptions getOptions(String categoryName) {
return optionsMgr.getOptions(categoryName); return optionsMgr.getOptions(categoryName);

View file

@ -58,12 +58,9 @@ public class OptionsManager implements OptionsService, OptionsChangeListener {
optionsMap.values().forEach(options -> options.dispose()); optionsMap.values().forEach(options -> options.dispose());
} }
/**
* Get the options for the given category name.
* @param category name of category
*/
@Override @Override
public ToolOptions getOptions(String category) { public ToolOptions getOptions(String category) {
ToolOptions opt = optionsMap.get(category); ToolOptions opt = optionsMap.get(category);
if (opt == null) { if (opt == null) {
opt = new ToolOptions(category); opt = new ToolOptions(category);
@ -87,20 +84,11 @@ public class OptionsManager implements OptionsService, OptionsChangeListener {
} }
} }
/**
* Return whether an Options object exists for the given category.
* @param category name of the category
* @return true if an Options object exists
*/
@Override @Override
public boolean hasOptions(String category) { public boolean hasOptions(String category) {
return optionsMap.containsKey(category); return optionsMap.containsKey(category);
} }
/**
* Shows Options Dialog with the section called 'category' being displayed
* @param category The category of options to have displayed
*/
@Override @Override
public void showOptionsDialog(String category, String filterText) { public void showOptionsDialog(String category, String filterText) {
if (optionsDialog != null && optionsDialog.isVisible()) { if (optionsDialog != null && optionsDialog.isVisible()) {
@ -112,9 +100,6 @@ public class OptionsManager implements OptionsService, OptionsChangeListener {
tool.showDialog(optionsDialog); tool.showDialog(optionsDialog);
} }
/**
* Get the list of options for all categories.
*/
@Override @Override
public ToolOptions[] getOptions() { public ToolOptions[] getOptions() {
ToolOptions[] opt = new ToolOptions[optionsMap.size()]; ToolOptions[] opt = new ToolOptions[optionsMap.size()];
@ -134,7 +119,6 @@ public class OptionsManager implements OptionsService, OptionsChangeListener {
* remove the options from the map. * remove the options from the map.
* @param ownerPlugin the owner plugin * @param ownerPlugin the owner plugin
*/ */
//TODO anyone using this Or should they be?
public void deregisterOwner(Plugin ownerPlugin) { public void deregisterOwner(Plugin ownerPlugin) {
List<String> deleteList = new ArrayList<>(); List<String> deleteList = new ArrayList<>();
Iterator<String> iter = optionsMap.keySet().iterator(); Iterator<String> iter = optionsMap.keySet().iterator();
@ -211,9 +195,6 @@ public class OptionsManager implements OptionsService, OptionsChangeListener {
} }
} }
/**
* Show the dialog to edit options.
*/
public void editOptions() { public void editOptions() {
if (optionsMap.isEmpty()) { if (optionsMap.isEmpty()) {
Msg.showInfo(getClass(), tool.getToolFrame(), "No Options", Msg.showInfo(getClass(), tool.getToolFrame(), "No Options",
@ -234,9 +215,6 @@ public class OptionsManager implements OptionsService, OptionsChangeListener {
} }
} }
/**
* Create the options dialog.
*/
private OptionsDialog createOptionsDialog() { private OptionsDialog createOptionsDialog() {
OptionsDialog dialog = null; OptionsDialog dialog = null;
if (optionsMap.size() == 0) { if (optionsMap.size() == 0) {
@ -275,37 +253,20 @@ public class OptionsManager implements OptionsService, OptionsChangeListener {
} }
private class OptionsComparator implements Comparator<ToolOptions> { private class OptionsComparator implements Comparator<ToolOptions> {
/**
* Compares its two arguments for order. Returns a negative integer,
* zero, or a positive integer as the first argument is less than, equal
* to, or greater than the second.<p>
*
* @param o1 the first object to be compared.
* @param o2 the second object to be compared.
* @return a negative integer, zero, or a positive integer as the
* first argument is less than, equal to, or greater than the
* second.
* @throws ClassCastException if the arguments' types prevent them from
* being compared by this Comparator.
*/
@Override @Override
public int compare(ToolOptions o1, ToolOptions o2) { public int compare(ToolOptions o1, ToolOptions o2) {
return o1.getName().compareTo(o2.getName()); return o1.getName().compareTo(o2.getName());
} }
} }
/////////////////////////////////////////////////////////////////////
private class KeyBindingOptionsEditor implements OptionsEditor { private class KeyBindingOptionsEditor implements OptionsEditor {
private KeyBindingsPanel panel; private KeyBindingsPanel panel;
KeyBindingOptionsEditor() { KeyBindingOptionsEditor() {
panel = new KeyBindingsPanel(tool, getOptions(ToolConstants.KEY_BINDINGS)); panel = new KeyBindingsPanel(tool, getOptions(DockingToolConstants.KEY_BINDINGS));
} }
/**
* Apply the changes.
*/
@Override @Override
public void apply() { public void apply() {
panel.apply(); panel.apply();
@ -326,9 +287,6 @@ public class OptionsManager implements OptionsService, OptionsChangeListener {
panel.dispose(); panel.dispose();
} }
/**
* Get the editor component.
*/
@Override @Override
public JComponent getEditorComponent(Options options, public JComponent getEditorComponent(Options options,
EditorStateFactory editorStateFactory) { EditorStateFactory editorStateFactory) {
@ -339,12 +297,10 @@ public class OptionsManager implements OptionsService, OptionsChangeListener {
public void setOptionsPropertyChangeListener(PropertyChangeListener listener) { public void setOptionsPropertyChangeListener(PropertyChangeListener listener) {
panel.setOptionsPropertyChangeListener(listener); panel.setOptionsPropertyChangeListener(listener);
} }
} }
@Override @Override
public void optionsChanged(ToolOptions options, String name, Object oldValue, Object newValue) { public void optionsChanged(ToolOptions options, String name, Object oldValue, Object newValue) {
tool.setConfigChanged(true); tool.setConfigChanged(true);
} }
} }

View file

@ -24,48 +24,54 @@ import ghidra.framework.options.ToolOptions;
*/ */
public interface OptionsService { public interface OptionsService {
/** /**
* Get the list of options for all categories. * Get the list of options for all categories.
* @return the list of options for all categories. * @return the list of options for all categories.
*/ */
public Options[] getOptions(); public Options[] getOptions();
/** /**
* Get the options for the given category name. * Get the options for the given category name. If no options exist with the given name,
* @param category name of category * then a new options object is created.
* @return the options for the given category name. *
*/ * <p>Note: the given name should not contains options path separator characters. Any
public ToolOptions getOptions(String category); * sub-options needed must be retrieved from the ToolOptions object returned from this
* method.
*
* @param category name of category
* @return the options for the given category name.
*/
public ToolOptions getOptions(String category);
/** /**
* Return whether an Options object exists for the given category. * Return whether an Options object exists for the given category.
* @param category name of the category * @param category name of the category
* @return true if an Options object exists * @return true if an Options object exists
*/ */
public boolean hasOptions(String category); public boolean hasOptions(String category);
/** /**
* Shows Options Dialog with the node denoted by "category" being displayed. The value is * Shows Options Dialog with the node denoted by "category" being displayed. The value is
* expected to be the name of a node in the options tree, residing under the root node. You * expected to be the name of a node in the options tree, residing under the root node. You
* may also provide the name of such a node, followed by the options delimiter, followed by * may also provide the name of such a node, followed by the options delimiter, followed by
* the name of a child node under that node. For example, suppose in the options tree exists * the name of a child node under that node. For example, suppose in the options tree exists
* a node {@literal Root->Foo} You may pass the value "Foo" to get that node. Or, suppose * a node {@literal Root->Foo} You may pass the value "Foo" to get that node. Or, suppose
* in the options tree exists a node {@literal Root->Foo->childNode1} In this case, you may * in the options tree exists a node {@literal Root->Foo->childNode1} In this case, you may
* pass the value "Foo.childNode1", where the '.' character is the delimiter of the * pass the value "Foo.childNode1", where the '.' character is the delimiter of the
* {@link ToolOptions} class (this is the value at the time of writing this documentation). * {@link ToolOptions} class (this is the value at the time of writing this documentation).
* *
* <p> * <p>
* The filter text parameter is used to set the contents filter text of the options. You may * The filter text parameter is used to set the contents filter text of the options. You may
* use this parameter to filter the tree; for example, to show only the node in the tree that * use this parameter to filter the tree; for example, to show only the node in the tree that
* you want the user to see. * you want the user to see.
* *
* @param category The category of options to have displayed * @param category The category of options to have displayed
* @param filterText An optional value used to filter the nodes visible in the options tree. * @param filterText An optional value used to filter the nodes visible in the options tree.
* You may pass <code>null</code> or the empty string <code>""</code> here if you * You may pass <code>null</code> or the empty string <code>""</code> here if you
* do not desire filtering. * do not desire filtering.
* @throws IllegalArgumentException if the given <code>category</code> value does not exist in * @throws IllegalArgumentException if the given <code>category</code> value does not exist in
* the tree of options. * the tree of options.
*/ */
public void showOptionsDialog(String category, String filterText); public void showOptionsDialog(String category, String filterText);
} }