mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-09-26 16:59:37 +08:00
GT-3019 - Function Graph - Added option for edge routing around vertices
This commit is contained in:
+3
-3
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+3
-2
@@ -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
|
||||
|
||||
+5
-20
@@ -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);
|
||||
}
|
||||
|
||||
+40
-11
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -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();
|
||||
}
|
||||
|
||||
+3
-1
@@ -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
|
||||
|
||||
+1
-1
@@ -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");
|
||||
|
||||
|
||||
+43
@@ -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);
|
||||
}
|
||||
+16
-6
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+31
-8
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
-5
@@ -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() {
|
||||
|
||||
+19
-4
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+53
@@ -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;
|
||||
}
|
||||
}
|
||||
+610
-131
File diff suppressed because it is too large
Load Diff
+12
-4
@@ -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
|
||||
|
||||
+3
-3
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+8
-4
@@ -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
|
||||
|
||||
+13
-4
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+13
@@ -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
|
||||
|
||||
+5
-5
@@ -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);
|
||||
}
|
||||
|
||||
+1
-1
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user