GT-3019 - Function Graph - Added option for edge routing around vertices

This commit is contained in:
dragonmacher
2019-07-25 18:40:21 -04:00
parent ac98e609d7
commit a04185e942
24 changed files with 933 additions and 259 deletions
@@ -32,8 +32,8 @@ import ghidra.util.exception.CancelledException;
public class SampleGraphPluginDependencyLayout
extends AbstractVisualGraphLayout<SampleVertex, SampleEdge> {
protected SampleGraphPluginDependencyLayout(SampleGraph graph) {
super(graph);
protected SampleGraphPluginDependencyLayout(SampleGraph graph, String name) {
super(graph, name);
}
@Override
@@ -50,7 +50,7 @@ public class SampleGraphPluginDependencyLayout
}
SampleGraphPluginDependencyLayout newLayout =
new SampleGraphPluginDependencyLayout((SampleGraph) newGraph);
new SampleGraphPluginDependencyLayout((SampleGraph) newGraph, getLayoutName());
return newLayout;
}
@@ -30,20 +30,21 @@ import resources.ResourceManager;
public class SampleGraphPluginDependencyLayoutProvider
extends AbstractLayoutProvider<SampleVertex, SampleEdge, SampleGraph> {
private static final String NAME = "Plugin Dependency Layout";
private static final Icon DEFAULT_ICON = ResourceManager.loadImage("images/color_swatch.png");
@Override
public VisualGraphLayout<SampleVertex, SampleEdge> getLayout(SampleGraph g, TaskMonitor monitor)
throws CancelledException {
SampleGraphPluginDependencyLayout layout = new SampleGraphPluginDependencyLayout(g);
SampleGraphPluginDependencyLayout layout = new SampleGraphPluginDependencyLayout(g, NAME);
initVertexLocations(g, layout);
return layout;
}
@Override
public String getLayoutName() {
return "Plugin Dependency Layout";
return NAME;
}
// Note: each provider really should load its own icon so that the toolbar item can
@@ -48,7 +48,6 @@ import ghidra.program.model.listing.Function;
import ghidra.program.util.ProgramLocation;
import ghidra.program.util.ProgramSelection;
import ghidra.util.*;
import ghidra.util.exception.AssertException;
import resources.Icons;
import resources.ResourceManager;
@@ -72,7 +71,6 @@ class FGActionManager {
private MultiStateDockingAction<EdgeDisplayType> vertexHoverModeAction;
private MultiStateDockingAction<EdgeDisplayType> vertexFocusModeAction;
private FGLayoutFinder layoutFinder = new DiscoverableFGLayoutFinder();
private MultiStateDockingAction<Class<? extends FGLayoutProvider>> layoutAction;
FGActionManager(FunctionGraphPlugin plugin, FGController controller, FGProvider provider) {
@@ -857,8 +855,7 @@ class FGActionManager {
HelpLocation layoutHelpLocation =
new HelpLocation("FunctionGraphPlugin", "Function_Graph_Action_Layout");
layoutAction = new MultiStateDockingAction<Class<? extends FGLayoutProvider>>(
"Relayout Graph", plugin.getName()) {
layoutAction = new MultiStateDockingAction<>("Relayout Graph", plugin.getName()) {
@Override
protected void doActionPerformed(ActionContext context) {
@@ -901,20 +898,12 @@ class FGActionManager {
private List<ActionState<Class<? extends FGLayoutProvider>>> loadActionStatesForLayoutProviders() {
Set<FGLayoutProvider> instances = layoutFinder.findLayouts();
if (instances.isEmpty()) {
throw new AssertException("Could not find any layout providers. You project may not " +
"be configured properly.");
}
List<FGLayoutProvider> layoutInstances = new ArrayList<>(instances);
Collections.sort(layoutInstances,
(o1, o2) -> -o1.getPriorityLevel() + o2.getPriorityLevel());
List<FGLayoutProvider> layoutInstances = plugin.getLayoutProviders();
List<ActionState<Class<? extends FGLayoutProvider>>> list = new ArrayList<>();
HelpLocation layoutHelpLocation =
new HelpLocation("FunctionGraphPlugin", "Function_Graph_Action_Layout");
for (FGLayoutProvider layout : layoutInstances) {
ActionState<Class<? extends FGLayoutProvider>> layoutState = new ActionState<>(
layout.getLayoutName(), layout.getActionIcon(), layout.getClass());
layoutState.setHelpLocation(layoutHelpLocation);
@@ -996,7 +985,7 @@ class FGActionManager {
offState.setHelpLocation(pathHelpLocation);
vertexHoverModeAction =
new MultiStateDockingAction<EdgeDisplayType>("Block Hover Mode", plugin.getName()) {
new MultiStateDockingAction<>("Block Hover Mode", plugin.getName()) {
@Override
public void actionStateChanged(ActionState<EdgeDisplayType> newActionState,
@@ -1065,7 +1054,7 @@ class FGActionManager {
offState.setHelpLocation(pathHelpLocation);
vertexFocusModeAction =
new MultiStateDockingAction<EdgeDisplayType>("Block Focus Mode", plugin.getName()) {
new MultiStateDockingAction<>("Block Focus Mode", plugin.getName()) {
@Override
public void actionStateChanged(ActionState<EdgeDisplayType> newActionState,
@@ -1204,10 +1193,6 @@ class FGActionManager {
}
}
void setLayoutFinder(FGLayoutFinder layoutFinder) {
this.layoutFinder = layoutFinder;
}
void setEdgeFocusMode(EdgeDisplayType edgeDisplayType) {
vertexFocusModeAction.setCurrentActionStateByUserData(edgeDisplayType);
}
@@ -27,6 +27,8 @@ import ghidra.app.events.*;
import ghidra.app.plugin.PluginCategoryNames;
import ghidra.app.plugin.ProgramPlugin;
import ghidra.app.plugin.core.colorizer.ColorizingService;
import ghidra.app.plugin.core.functiongraph.graph.layout.FGLayoutOptions;
import ghidra.app.plugin.core.functiongraph.graph.layout.FGLayoutProvider;
import ghidra.app.plugin.core.functiongraph.mvc.FunctionGraphOptions;
import ghidra.app.services.*;
import ghidra.app.util.viewer.format.FormatManager;
@@ -40,6 +42,7 @@ import ghidra.graph.viewer.options.VisualGraphOptions;
import ghidra.program.model.listing.Program;
import ghidra.program.util.ProgramLocation;
import ghidra.program.util.ProgramSelection;
import ghidra.util.exception.AssertException;
import resources.ResourceManager;
//@formatter:off
@@ -78,6 +81,7 @@ public class FunctionGraphPlugin extends ProgramPlugin implements OptionsChangeL
private FunctionGraphOptions functionGraphOptions = new FunctionGraphOptions();
private FGColorProvider colorProvider;
private List<FGLayoutProvider> layoutProviders;
public FunctionGraphPlugin(PluginTool tool) {
super(tool, true, true, true);
@@ -88,6 +92,9 @@ public class FunctionGraphPlugin extends ProgramPlugin implements OptionsChangeL
@Override
protected void init() {
super.init();
layoutProviders = loadLayoutProviders();
createNewProvider();
initializeOptions();
@@ -125,17 +132,44 @@ public class FunctionGraphPlugin extends ProgramPlugin implements OptionsChangeL
}
}
private List<FGLayoutProvider> loadLayoutProviders() {
FGLayoutFinder layoutFinder = new DiscoverableFGLayoutFinder();
Set<FGLayoutProvider> instances = layoutFinder.findLayouts();
if (instances.isEmpty()) {
throw new AssertException("Could not find any layout providers. You project may not " +
"be configured properly.");
}
List<FGLayoutProvider> layouts = new ArrayList<>(instances);
Collections.sort(layouts, (o1, o2) -> -o1.getPriorityLevel() + o2.getPriorityLevel());
return layouts;
}
private void initializeOptions() {
ToolOptions options = tool.getOptions(PLUGIN_OPTIONS_NAME);
options.addOptionsChangeListener(this);
functionGraphOptions.initializeOptions(this, options);
functionGraphOptions.loadOptions(this, options);
functionGraphOptions.registerOptions(options);
functionGraphOptions.loadOptions(options);
for (FGLayoutProvider layoutProvider : layoutProviders) {
String layoutName = layoutProvider.getLayoutName();
Options layoutToolOptions = options.getOptions(layoutName);
FGLayoutOptions layoutOptions = layoutProvider.createLayoutOptions(layoutToolOptions);
if (layoutOptions == null) {
continue; // many layouts do not have options
}
layoutOptions.registerOptions(layoutToolOptions);
layoutOptions.loadOptions(layoutToolOptions);
functionGraphOptions.setLayoutOptions(layoutName, layoutOptions);
}
}
@Override
public void optionsChanged(ToolOptions options, String optionName, Object oldValue,
Object newValue) {
functionGraphOptions.loadOptions(this, options);
functionGraphOptions.loadOptions(options);
connectedProvider.getComponent().repaint();
for (FGProvider provider : disconnectedProviders) {
provider.getComponent().repaint();
@@ -383,14 +417,6 @@ public class FunctionGraphPlugin extends ProgramPlugin implements OptionsChangeL
}
}
@Override
public void dataStateRestoreCompleted() {
super.dataStateRestoreCompleted();
// ProgramLocation location = ProgramLocation.getLocation(
// currentProgram, dataSaveState, null );
}
public FGColorProvider getColorProvider() {
return colorProvider;
}
@@ -399,4 +425,7 @@ public class FunctionGraphPlugin extends ProgramPlugin implements OptionsChangeL
return functionGraphOptions;
}
public List<FGLayoutProvider> getLayoutProviders() {
return Collections.unmodifiableList(layoutProviders);
}
}
@@ -37,8 +37,8 @@ public abstract class AbstractFGLayout extends AbstractVisualGraphLayout<FGVerte
protected Function function;
protected FunctionGraphOptions options;
protected AbstractFGLayout(FunctionGraph graph) {
super(graph);
protected AbstractFGLayout(FunctionGraph graph, String layoutName) {
super(graph, layoutName);
this.function = graph.getFunction();
this.options = graph.getOptions();
}
@@ -34,8 +34,10 @@ import ghidra.util.task.TaskMonitor;
public class EmptyLayout extends AbstractVisualGraphLayout<FGVertex, FGEdge> implements FGLayout {
private static final String NAME = "Empty Layout";
public EmptyLayout(FunctionGraph graph) {
super(graph);
super(graph, NAME);
}
@Override
@@ -19,7 +19,7 @@ import javax.swing.Icon;
import resources.ResourceManager;
public abstract class ExperimentalLayoutProvider implements FGLayoutProvider {
public abstract class ExperimentalLayoutProvider extends FGLayoutProvider {
private static final Icon ICON = ResourceManager.loadImage("images/package_development.png");
@@ -0,0 +1,43 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.functiongraph.graph.layout;
import ghidra.app.plugin.core.functiongraph.FunctionGraphPlugin;
import ghidra.framework.options.Options;
/**
* An interface for {@link FGLayout} options
*/
public interface FGLayoutOptions {
public static final String OWNER = FunctionGraphPlugin.class.getSimpleName();
/**
* Called during setup for this class to register its options with the given {@link Options}
* object
*
* @param options the tool options
*/
public void registerOptions(Options options);
/**
* Called when the given {@link Options} object has changed. This class will update its
* options with the values from the given options object.
*
* @param options the tool options
*/
public void loadOptions(Options options);
}
@@ -18,20 +18,30 @@ package ghidra.app.plugin.core.functiongraph.graph.layout;
import ghidra.app.plugin.core.functiongraph.graph.FGEdge;
import ghidra.app.plugin.core.functiongraph.graph.FunctionGraph;
import ghidra.app.plugin.core.functiongraph.graph.vertex.FGVertex;
import ghidra.framework.options.Options;
import ghidra.graph.viewer.layout.LayoutProvider;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
public interface FGLayoutProvider extends LayoutProvider<FGVertex, FGEdge, FunctionGraph> {
public abstract class FGLayoutProvider implements LayoutProvider<FGVertex, FGEdge, FunctionGraph> {
public abstract FGLayout getFGLayout(FunctionGraph graph, TaskMonitor monitor)
throws CancelledException;
// Suppressing warning on the return type; we know our class is the right type
@Override
public default FGLayout getLayout(FunctionGraph graph, TaskMonitor monitor)
throws CancelledException {
public FGLayout getLayout(FunctionGraph graph, TaskMonitor monitor) throws CancelledException {
return getFGLayout(graph, monitor);
}
public FGLayout getFGLayout(FunctionGraph graph, TaskMonitor monitor) throws CancelledException;
/**
* Creates an options object for layouts created by this provider. Returns null if there
* are not options for layouts created by this provider.
*
* @param options the tool options into which layout options should be registered
* @return the new options; null if there are no options
*/
public FGLayoutOptions createLayoutOptions(Options options) {
return null;
}
}
@@ -16,15 +16,20 @@
package ghidra.app.plugin.core.functiongraph.mvc;
import java.awt.Color;
import java.util.*;
import java.util.Map.Entry;
import ghidra.framework.options.ToolOptions;
import ghidra.framework.plugintool.Plugin;
import ghidra.app.plugin.core.functiongraph.FunctionGraphPlugin;
import ghidra.app.plugin.core.functiongraph.graph.layout.FGLayoutOptions;
import ghidra.framework.options.Options;
import ghidra.graph.viewer.options.*;
import ghidra.program.model.symbol.FlowType;
import ghidra.util.HelpLocation;
public class FunctionGraphOptions extends VisualGraphOptions {
protected static final String OWNER = FunctionGraphPlugin.class.getSimpleName();
private static final String EDGE_FALLTHROUGH_HIGHLIGHT_COLOR_KEY =
"Edge Color - Fallthrough Highlight";
private static final String EDGE_UNCONDITIONAL_JUMP_HIGHLIGHT_COLOR_KEY =
@@ -48,7 +53,7 @@ public class FunctionGraphOptions extends VisualGraphOptions {
private static final String DEFAULT_GROUP_BACKGROUND_COLOR_KEY = "Default Group Color";
private static final String DEFAULT_GROUP_BACKGROUND_COLOR_DESCRPTION =
"The default " + "background color applied to newly created group vertices";
"The default background color applied to newly created group vertices";
private static final String UPDATE_GROUP_AND_UNGROUP_COLORS =
"Update Vertex Colors When Grouping";
@@ -74,6 +79,8 @@ public class FunctionGraphOptions extends VisualGraphOptions {
protected RelayoutOption relayoutOption = RelayoutOption.NEVER;
private Map<String, FGLayoutOptions> layoutOptionsByName = new HashMap<>();
public Color getDefaultGroupBackgroundColor() {
return defaultGroupBackgroundColor;
}
@@ -110,8 +117,9 @@ public class FunctionGraphOptions extends VisualGraphOptions {
return relayoutOption;
}
public void initializeOptions(Plugin plugin, ToolOptions options) {
HelpLocation help = new HelpLocation(plugin.getName(), "Options");
public void registerOptions(Options options) {
HelpLocation help = new HelpLocation(OWNER, "Options");
options.setOptionsHelpLocation(help);
options.registerOption(RELAYOUT_OPTIONS_KEY, RelayoutOption.VERTEX_GROUPING_CHANGES, help,
@@ -124,8 +132,7 @@ public class FunctionGraphOptions extends VisualGraphOptions {
USE_MOUSE_RELATIVE_ZOOM_DESCRIPTION);
options.registerOption(USE_CONDENSED_LAYOUT, useCondensedLayout(),
new HelpLocation(plugin.getName(), "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);
@@ -161,7 +168,7 @@ public class FunctionGraphOptions extends VisualGraphOptions {
}
public void loadOptions(Plugin plugin, ToolOptions options) {
public void loadOptions(Options options) {
conditionalJumpEdgeColor =
options.getColor(EDGE_COLOR_CONDITIONAL_JUMP_KEY, conditionalJumpEdgeColor);
@@ -198,6 +205,14 @@ public class FunctionGraphOptions extends VisualGraphOptions {
updateGroupColorsAutomatically =
options.getBoolean(UPDATE_GROUP_AND_UNGROUP_COLORS, updateGroupColorsAutomatically);
Set<Entry<String, FGLayoutOptions>> entries = layoutOptionsByName.entrySet();
for (Entry<String, FGLayoutOptions> entry : entries) {
String layoutName = entry.getKey();
FGLayoutOptions layoutOptions = entry.getValue();
Options layoutToolOptions = options.getOptions(layoutName);
layoutOptions.loadOptions(layoutToolOptions);
}
}
public Color getColor(FlowType flowType) {
@@ -227,4 +242,12 @@ public class FunctionGraphOptions extends VisualGraphOptions {
return Color.BLACK;
}
public FGLayoutOptions getLayoutOptions(String layoutName) {
return layoutOptionsByName.get(layoutName);
}
public void setLayoutOptions(String layoutName, FGLayoutOptions options) {
layoutOptionsByName.put(layoutName, options);
}
}
@@ -53,7 +53,6 @@ import ghidra.app.plugin.core.clipboard.ClipboardPlugin;
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
import ghidra.app.plugin.core.functiongraph.graph.*;
import ghidra.app.plugin.core.functiongraph.graph.layout.FGLayoutProvider;
import ghidra.app.plugin.core.functiongraph.graph.layout.TestFGLayoutProvider;
import ghidra.app.plugin.core.functiongraph.graph.vertex.*;
import ghidra.app.plugin.core.functiongraph.mvc.*;
import ghidra.app.services.*;
@@ -75,7 +74,6 @@ import ghidra.program.util.ProgramSelection;
import ghidra.test.*;
import ghidra.util.Msg;
import ghidra.util.task.RunManager;
import util.CollectionUtils;
public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedIntegrationTest {
@@ -558,9 +556,6 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
graphProvider = waitForComponentProvider(FGProvider.class);
assertNotNull("Graph not shown", graphProvider);
FGActionManager actionManager = graphProvider.getActionManager();
actionManager.setLayoutFinder(() -> CollectionUtils.asSet(new TestFGLayoutProvider()));
}
protected ProgramSelection makeSingleVertexSelectionInCodeBrowser() {
@@ -25,6 +25,7 @@ import javax.swing.Icon;
import ghidra.app.plugin.core.functiongraph.graph.FGEdge;
import ghidra.app.plugin.core.functiongraph.graph.FunctionGraph;
import ghidra.app.plugin.core.functiongraph.graph.vertex.FGVertex;
import ghidra.app.plugin.core.functiongraph.graph.vertex.GroupedFunctionGraphVertex;
import ghidra.graph.VisualGraph;
import ghidra.graph.viewer.layout.*;
import ghidra.graph.viewer.vertex.VisualGraphVertexShapeTransformer;
@@ -37,13 +38,14 @@ import resources.Icons;
/**
* A simple layout that is used during testing
*/
public class TestFGLayoutProvider implements FGLayoutProvider {
public class TestFGLayoutProvider extends FGLayoutProvider {
private static final String NAME = "Test Layout";
private static final int VERTEX_TO_EDGE_ARTICULATION_OFFSET = 20;
@Override
public String getLayoutName() {
return "Test Layout";
return NAME;
}
@Override
@@ -65,7 +67,7 @@ public class TestFGLayoutProvider implements FGLayoutProvider {
private class TestFGLayout extends AbstractFGLayout {
protected TestFGLayout(FunctionGraph graph) {
super(graph);
super(graph, NAME);
}
@Override
@@ -224,7 +226,9 @@ public class TestFGLayoutProvider implements FGLayoutProvider {
treeify(g, left, nodesByVertices);
break;
default:
Msg.debug(this, "\n\n\tMore than 2 edges????: " + parent);
if (!(parent.v instanceof GroupedFunctionGraphVertex)) {
Msg.debug(this, "\n\n\tMore than 2 edges????: " + parent);
}
}
}
@@ -357,5 +361,16 @@ public class TestFGLayoutProvider implements FGLayoutProvider {
Node(FGVertex v) {
this.v = v;
}
@Override
public String toString() {
//@formatter:off
return "{\n" +
"\tv: " + v + ",\n" +
"\tleft: " + left + ",\n" +
"\tright: " + right + "\n" +
"}";
//@formatter:on
}
}
}
@@ -0,0 +1,53 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.functiongraph.graph.layout;
import ghidra.framework.options.Options;
import ghidra.util.HelpLocation;
/**
* Options for the {@link DecompilerNestedLayout}
*/
public class DNLayoutOptions implements FGLayoutOptions {
private static final String USE_EDGE_ROUTING_AROUND_VERTICES_KEY =
"Route Edges Around Vertices";
private static final String USE_EDGE_ROUTING_AROUND_VERTICES_DESCRIPTION = "Signals that " +
"edges should be routed around any intersecting vertex. When toggled off, edges will " +
"pass through any intersecting vertices.";
private boolean useEdgeRoutingAroundVertices;
@Override
public void registerOptions(Options options) {
// TODO layout-specific help
HelpLocation help = new HelpLocation(OWNER, "Options");
options.registerOption(USE_EDGE_ROUTING_AROUND_VERTICES_KEY, useEdgeRoutingAroundVertices,
help, USE_EDGE_ROUTING_AROUND_VERTICES_DESCRIPTION);
}
@Override
public void loadOptions(Options options) {
useEdgeRoutingAroundVertices =
options.getBoolean(USE_EDGE_ROUTING_AROUND_VERTICES_KEY, useEdgeRoutingAroundVertices);
}
public boolean useEdgeRoutingAroundVertices() {
return useEdgeRoutingAroundVertices;
}
}
@@ -18,25 +18,33 @@ package ghidra.app.plugin.core.functiongraph.graph.layout;
import javax.swing.Icon;
import ghidra.app.plugin.core.functiongraph.graph.FunctionGraph;
import ghidra.framework.options.Options;
import ghidra.util.task.TaskMonitor;
import resources.ResourceManager;
public class DecompilerNestedLayoutProvider implements FGLayoutProvider {
public class DecompilerNestedLayoutProvider extends FGLayoutProvider {
private static final Icon ICON =
ResourceManager.loadImage("images/function_graph_code_flow.png");
static final String LAYOUT_NAME = "Nested Code Layout";
@Override
public FGLayout getFGLayout(FunctionGraph graph, TaskMonitor monitor) {
DecompilerNestedLayout layout = new DecompilerNestedLayout(graph);
DecompilerNestedLayout layout = new DecompilerNestedLayout(graph, LAYOUT_NAME);
layout.setTaskMonitor(monitor);
return layout;
}
@Override
public FGLayoutOptions createLayoutOptions(Options options) {
DNLayoutOptions layoutOptions = new DNLayoutOptions();
layoutOptions.registerOptions(options);
return layoutOptions;
}
@Override
public String getLayoutName() {
// TODO better name?...or rename classes to match
return "Nested Code Layout";
return LAYOUT_NAME;
}
@Override
@@ -44,8 +44,8 @@ import ghidra.util.task.TaskMonitor;
*/
public class BowTieLayout extends AbstractVisualGraphLayout<FcgVertex, FcgEdge> {
protected BowTieLayout(FunctionCallGraph graph) {
super(graph);
protected BowTieLayout(FunctionCallGraph graph, String name) {
super(graph, name);
}
@Override
@@ -58,7 +58,7 @@ public class BowTieLayout extends AbstractVisualGraphLayout<FcgVertex, FcgEdge>
getClass().getSimpleName());
}
BowTieLayout newLayout = new BowTieLayout((FunctionCallGraph) newGraph);
BowTieLayout newLayout = new BowTieLayout((FunctionCallGraph) newGraph, getLayoutName());
return newLayout;
}
@@ -38,7 +38,7 @@ public class BowTieLayoutProvider
public VisualGraphLayout<FcgVertex, FcgEdge> getLayout(FunctionCallGraph graph,
TaskMonitor monitor) throws CancelledException {
BowTieLayout layout = new BowTieLayout(graph);
BowTieLayout layout = new BowTieLayout(graph, NAME);
initVertexLocations(graph, layout);
return layout;
}
@@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,15 +15,17 @@
*/
package docking.menu;
import javax.swing.Icon;
import ghidra.util.HelpLocation;
import ghidra.util.SystemUtilities;
import javax.swing.Icon;
/**
* Note: this class overrides the {@ #equals(Object)} method} and relies upon the <tt>equals</tt>
* Note: this class overrides the <tt>equals(Object)</tt> and relies upon the <tt>equals</tt>
* method of the <tt>userData</tt> object. Thus, if it is important that equals work for you in
* the non-standard identity way, then you must override <tt>equals</tt> in your user data obejcts.
* the non-standard identity way, then you must override <tt>equals</tt> in your user data objects.
*
* @param <T> the type of the action state
*/
public class ActionState<T> {
@@ -32,55 +33,60 @@ public class ActionState<T> {
private final Icon icon;
private T userData;
private HelpLocation helpLocation;
public ActionState(String name, Icon icon, T userData) {
this.name = name;
this.icon = icon;
this.userData = userData;
this.userData = userData;
}
public String getName() {
return name;
}
public Icon getIcon() {
return icon;
}
public T getUserData() {
return userData;
}
public void setHelpLocation( HelpLocation helpLocation ) {
this.helpLocation = helpLocation;
}
public HelpLocation getHelpLocation() {
return helpLocation;
}
@Override
public boolean equals( Object other ) {
if ( other == null ) {
return false;
}
Class<? extends Object> otherClass = other.getClass();
if ( !getClass().equals( otherClass ) ) {
return false;
}
ActionState<?> otherState = (ActionState<?>) other;
if ( !SystemUtilities.isEqual( userData, otherState.userData ) ) {
return false;
}
return name.equals( otherState.name );
}
@Override
public int hashCode() {
return name.hashCode() + ((userData == null) ? 0 : userData.hashCode());
}
public void setHelpLocation(HelpLocation helpLocation) {
this.helpLocation = helpLocation;
}
public HelpLocation getHelpLocation() {
return helpLocation;
}
@Override
public boolean equals(Object other) {
if (other == null) {
return false;
}
Class<? extends Object> otherClass = other.getClass();
if (!getClass().equals(otherClass)) {
return false;
}
ActionState<?> otherState = (ActionState<?>) other;
if (!SystemUtilities.isEqual(userData, otherState.userData)) {
return false;
}
return name.equals(otherState.name);
}
@Override
public int hashCode() {
return name.hashCode() + ((userData == null) ? 0 : userData.hashCode());
}
@Override
public String toString() {
return name + ": " + userData;
}
}
@@ -76,11 +76,15 @@ public class VisualGraphEdgeSelectionGraphMousePlugin<V extends VisualVertex, E
private void pickAndShowVertex(V vertex, PickedState<V> pickedVertexState,
GraphViewer<V, E> viewer) {
GPickedState<V> pickedStateWrapper = (GPickedState<V>) pickedVertexState;
pickedStateWrapper.pickToActivate(vertex);
VisualGraphViewUpdater<V, E> updater = viewer.getViewUpdater();
updater.moveVertexToCenterWithAnimation(vertex);
updater.moveVertexToCenterWithAnimation(vertex, isBusy -> {
// pick the vertex after the animation has run
if (!isBusy) {
GPickedState<V> pickedStateWrapper = (GPickedState<V>) pickedVertexState;
pickedStateWrapper.pickToActivate(vertex);
}
});
}
@Override
@@ -33,7 +33,6 @@ import ghidra.graph.VisualGraph;
import ghidra.graph.viewer.*;
import ghidra.graph.viewer.layout.LayoutListener.ChangeType;
import ghidra.graph.viewer.renderer.ArticulatedEdgeRenderer;
import ghidra.graph.viewer.renderer.VisualGraphRenderer;
import ghidra.graph.viewer.shape.ArticulatedEdgeTransformer;
import ghidra.graph.viewer.vertex.VisualGraphVertexShapeTransformer;
import ghidra.util.datastruct.WeakDataStructureFactory;
@@ -87,12 +86,22 @@ public abstract class AbstractVisualGraphLayout<V extends VisualVertex,
new ArticulatedEdgeTransformer<>();
private ArticulatedEdgeRenderer<V, E> edgeRenderer = new ArticulatedEdgeRenderer<>();
protected TaskMonitor monitor = TaskMonitor.DUMMY;
protected String layoutName;
protected boolean layoutInitialized;
protected AbstractVisualGraphLayout(Graph<V, E> graph) {
protected TaskMonitor monitor = TaskMonitor.DUMMY;
protected AbstractVisualGraphLayout(Graph<V, E> graph, String layoutName) {
super(graph);
this.layoutName = layoutName;
}
/**
* Returns the name of this layout
* @return the name of this layout
*/
public String getLayoutName() {
return layoutName;
}
/**
@@ -99,6 +99,18 @@ public class LayoutLocationMap<V, E> {
return doGetColumn(gridX);
}
public Column getColumnContaining(int x) {
Column column = null;
Collection<Column> values = columnsByIndex.values();
for (Column nextColumn : values) {
column = nextColumn;
if (x < column.x) {
return column;
}
}
return column;
}
private Column doGetColumn(int index) {
Column column = columnsByIndex.get(index);
if (column == null) {
@@ -342,4 +354,5 @@ public class LayoutLocationMap<V, E> {
offset += column.getPaddedWidth(isCondensed);
}
}
}
@@ -48,7 +48,6 @@ public interface LayoutProvider<V extends VisualVertex,
/**
* Returns a new instance of the layout that this class provides
*
* @param m the model object
* @param graph the graph
* @param monitor a task monitor
* @return the new layout
@@ -82,11 +82,11 @@ public class VisualGraphRenderer<V extends VisualVertex, E extends VisualEdge<V>
// paint all the edges
// DEBUG code to show the edges *over* the vertices
// for (E e : layout.getGraph().getEdges()) {
//
// renderEdge(renderContext, layout, e);
// renderEdgeLabel(renderContext, layout, e);
// }
for (E e : layout.getGraph().getEdges()) {
renderEdge(renderContext, layout, e);
renderEdgeLabel(renderContext, layout, e);
}
paintLayoutGridCells(renderContext, layout);
}
@@ -273,7 +273,7 @@ public class TestGraphAlgorithmSteppingViewerPanel<V, E extends GEdge<V>> extend
AbstractVisualGraphLayout<AlgorithmTestSteppingVertex<V>, AlgorithmTestSteppingEdge<V>> {
protected TestGraphLayout(TestGraph graph) {
super(graph);
super(graph, "Test Layout");
}
@SuppressWarnings("unchecked")