mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-06 03:50:02 +02:00
Fixes for GP-377, GP-554, GP-551, GP-552, GP-553.
This commit removes previous hacks for overlapping vertices, integrates a configurable graph mouse model, puts back the RTree spatial data structures.
This commit is contained in:
parent
5c98e46d62
commit
a4dbc30047
23 changed files with 320 additions and 209 deletions
|
@ -147,10 +147,10 @@ public class GraphDisplayBrokerPlugin extends Plugin
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GraphDisplay getDefaultGraphDisplay(boolean reuseGraph,
|
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, monitor);
|
return defaultGraphDisplayProvider.getGraphDisplay(reuseGraph, properties, monitor);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,9 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.services;
|
package ghidra.app.services;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import ghidra.app.plugin.core.graph.GraphDisplayBrokerListener;
|
import ghidra.app.plugin.core.graph.GraphDisplayBrokerListener;
|
||||||
import ghidra.app.plugin.core.graph.GraphDisplayBrokerPlugin;
|
import ghidra.app.plugin.core.graph.GraphDisplayBrokerPlugin;
|
||||||
|
@ -60,7 +62,12 @@ public interface GraphDisplayBroker {
|
||||||
* @return a {@link GraphDisplay} object to sends graphs to be displayed or exported.
|
* @return a {@link GraphDisplay} object to sends graphs to be displayed or exported.
|
||||||
* @throws GraphException thrown if an error occurs trying to get a graph display
|
* @throws GraphException thrown if an error occurs trying to get a graph display
|
||||||
*/
|
*/
|
||||||
public GraphDisplay getDefaultGraphDisplay(boolean reuseGraph, TaskMonitor monitor)
|
default GraphDisplay getDefaultGraphDisplay(boolean reuseGraph, TaskMonitor monitor)
|
||||||
|
throws GraphException {
|
||||||
|
return getDefaultGraphDisplay(reuseGraph, Collections.emptyMap(), monitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
GraphDisplay getDefaultGraphDisplay(boolean reuseGraph, Map<String, String> properties, TaskMonitor monitor)
|
||||||
throws GraphException;
|
throws GraphException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -30,6 +30,7 @@ import ghidra.program.model.listing.Program;
|
||||||
import ghidra.program.model.pcode.*;
|
import ghidra.program.model.pcode.*;
|
||||||
import ghidra.service.graph.*;
|
import ghidra.service.graph.*;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
public class GraphAST extends GhidraScript {
|
public class GraphAST extends GhidraScript {
|
||||||
protected static final String COLOR_ATTRIBUTE = "Color";
|
protected static final String COLOR_ATTRIBUTE = "Color";
|
||||||
|
@ -64,8 +65,14 @@ public class GraphAST extends GhidraScript {
|
||||||
graph = new AttributedGraph();
|
graph = new AttributedGraph();
|
||||||
buildGraph();
|
buildGraph();
|
||||||
|
|
||||||
|
Map<String, String> properties = new HashMap<>();
|
||||||
|
properties.put("selectedVertexColor", "0xFF1493");
|
||||||
|
properties.put("selectedEdgeColor", "0xFF1493");
|
||||||
|
properties.put("initialLayoutAlgorithm", "Hierarchical MinCross Coffman Graham");
|
||||||
|
properties.put("displayVerticesAsIcons", "false");
|
||||||
|
properties.put("vertexLabelPosition", "S");
|
||||||
GraphDisplay graphDisplay =
|
GraphDisplay graphDisplay =
|
||||||
graphDisplayBroker.getDefaultGraphDisplay(false, monitor);
|
graphDisplayBroker.getDefaultGraphDisplay(false, properties, monitor);
|
||||||
// graphDisplay.defineVertexAttribute(CODE_ATTRIBUTE); //
|
// graphDisplay.defineVertexAttribute(CODE_ATTRIBUTE); //
|
||||||
// graphDisplay.defineVertexAttribute(SYMBOLS_ATTRIBUTE);
|
// graphDisplay.defineVertexAttribute(SYMBOLS_ATTRIBUTE);
|
||||||
// graphDisplay.defineEdgeAttribute(EDGE_TYPE_ATTRIBUTE);
|
// graphDisplay.defineEdgeAttribute(EDGE_TYPE_ATTRIBUTE);
|
||||||
|
|
|
@ -15,7 +15,9 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.core.decompile.actions;
|
package ghidra.app.plugin.core.decompile.actions;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import docking.widgets.EventTrigger;
|
import docking.widgets.EventTrigger;
|
||||||
import ghidra.app.services.GraphDisplayBroker;
|
import ghidra.app.services.GraphDisplayBroker;
|
||||||
|
@ -101,7 +103,11 @@ public class ASTGraphTask extends Task {
|
||||||
else {
|
else {
|
||||||
createControlFlowGraph(graph, monitor);
|
createControlFlowGraph(graph, monitor);
|
||||||
}
|
}
|
||||||
GraphDisplay display = graphService.getDefaultGraphDisplay(!newGraph, monitor);
|
Map<String, String> properties = new HashMap<>();
|
||||||
|
properties.put("selectedVertexColor", "0xFF1493");
|
||||||
|
properties.put("selectedEdgeColor", "0xFF1493");
|
||||||
|
properties.put("initialLayoutAlgorithm", "Hierarchical MinCross Coffman Graham");
|
||||||
|
GraphDisplay display = graphService.getDefaultGraphDisplay(!newGraph, properties, monitor);
|
||||||
ASTGraphDisplayListener displayListener =
|
ASTGraphDisplayListener displayListener =
|
||||||
new ASTGraphDisplayListener(tool, display, hfunction, graphType);
|
new ASTGraphDisplayListener(tool, display, hfunction, graphType);
|
||||||
display.setGraphDisplayListener(displayListener);
|
display.setGraphDisplayListener(displayListener);
|
||||||
|
|
|
@ -11,12 +11,15 @@ eclipse.project.name = 'Features Graph Services'
|
||||||
dependencies {
|
dependencies {
|
||||||
compile project(":Base")
|
compile project(":Base")
|
||||||
|
|
||||||
compile "com.github.tomnelson:jungrapht-visualization:1.0"
|
compile "com.github.tomnelson:jungrapht-visualization:1.1"
|
||||||
compile "com.github.tomnelson:jungrapht-layout:1.0"
|
compile "com.github.tomnelson:jungrapht-layout:1.1"
|
||||||
|
compile "org.jgrapht:jgrapht-core:1.5.0"
|
||||||
runtime "org.slf4j:slf4j-api:1.7.25"
|
|
||||||
|
// not using jgrapht-io code that depends on antlr, so exclude antlr
|
||||||
|
compile ("org.jgrapht:jgrapht-io:1.5.0") { exclude group: "org.antlr", module: "antlr4-runtime" }
|
||||||
|
runtime "org.slf4j:slf4j-api:1.7.30"
|
||||||
// use this if you want no slf4j log messages
|
// use this if you want no slf4j log messages
|
||||||
runtime "org.slf4j:slf4j-nop:1.7.25"
|
runtime "org.slf4j:slf4j-nop:1.7.30"
|
||||||
// use this if you want slf4j log messages sent to log4j
|
// use this if you want slf4j log messages sent to log4j
|
||||||
// runtime "org.apache.logging.log4j:log4j-slf4j-impl:2.12.1"
|
// runtime "org.apache.logging.log4j:log4j-slf4j-impl:2.12.1"
|
||||||
runtime "org.jheaps:jheaps:0.13"
|
runtime "org.jheaps:jheaps:0.13"
|
||||||
|
|
|
@ -87,6 +87,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
|
|
||||||
private Logger log = Logger.getLogger(DefaultGraphDisplay.class.getName());
|
private Logger log = Logger.getLogger(DefaultGraphDisplay.class.getName());
|
||||||
|
|
||||||
|
private Map<String, String> graphDisplayProperties = new HashMap<>();
|
||||||
private GraphDisplayListener listener = new DummyGraphDisplayListener();
|
private GraphDisplayListener listener = new DummyGraphDisplayListener();
|
||||||
private String title;
|
private String title;
|
||||||
|
|
||||||
|
@ -164,15 +165,18 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
private PopupRegulator<AttributedVertex, AttributedEdge> popupRegulator;
|
private PopupRegulator<AttributedVertex, AttributedEdge> popupRegulator;
|
||||||
private GhidraGraphCollapser graphCollapser;
|
private GhidraGraphCollapser graphCollapser;
|
||||||
|
|
||||||
|
private Set<DockingAction> addedActions = new LinkedHashSet<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create the initial display, the graph-less visualization viewer, and its controls
|
* Create the initial display, the graph-less visualization viewer, and its controls
|
||||||
* @param displayProvider provides a {@link PluginTool} for Docking features
|
* @param displayProvider provides a {@link PluginTool} for Docking features
|
||||||
* @param id the unique display id
|
* @param id the unique display id
|
||||||
*/
|
*/
|
||||||
DefaultGraphDisplay(DefaultGraphDisplayProvider displayProvider, int id) {
|
DefaultGraphDisplay(DefaultGraphDisplayProvider displayProvider, Map<String, String> graphDisplayProperties, int id) {
|
||||||
this.graphDisplayProvider = displayProvider;
|
this.graphDisplayProvider = displayProvider;
|
||||||
this.displayId = id;
|
this.displayId = id;
|
||||||
this.pluginTool = graphDisplayProvider.getPluginTool();
|
this.pluginTool = graphDisplayProvider.getPluginTool();
|
||||||
|
this.graphDisplayProperties = graphDisplayProperties;
|
||||||
this.viewer = createViewer();
|
this.viewer = createViewer();
|
||||||
buildHighlighers();
|
buildHighlighers();
|
||||||
|
|
||||||
|
@ -206,6 +210,28 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
connectSelectionStateListeners();
|
connectSelectionStateListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setProperty(String key, String value) {
|
||||||
|
this.graphDisplayProperties.put(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getProperty(String key) {
|
||||||
|
return graphDisplayProperties.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getPropertyMap() {
|
||||||
|
return Collections.unmodifiableMap(graphDisplayProperties);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Color getSelectedVertexColor() {
|
||||||
|
return Colors.getHexColor(graphDisplayProperties.getOrDefault("selectedVertexColor",
|
||||||
|
"0xFF0000"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Color getSelectedEdgeColor() {
|
||||||
|
return Colors.getHexColor(graphDisplayProperties.getOrDefault("selectedEdgeColor", "0xFF0000"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
JComponent getComponent() {
|
JComponent getComponent() {
|
||||||
JComponent component = viewer.getComponent();
|
JComponent component = viewer.getComponent();
|
||||||
component.setFocusable(true);
|
component.setFocusable(true);
|
||||||
|
@ -234,7 +260,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
// this lens' delegate is the viewer's VIEW layer
|
// this lens' delegate is the viewer's VIEW layer
|
||||||
.delegate(transformer)
|
.delegate(transformer)
|
||||||
.build();
|
.build();
|
||||||
LensGraphMouse lensGraphMouse = new DefaultLensGraphMouse<>(magnificationPlugin);
|
LensGraphMouse lensGraphMouse = DefaultLensGraphMouse.builder().magnificationPlugin(magnificationPlugin).build();
|
||||||
return MagnifyImageLensSupport.builder(viewer)
|
return MagnifyImageLensSupport.builder(viewer)
|
||||||
.lensTransformer(shapeTransformer)
|
.lensTransformer(shapeTransformer)
|
||||||
.lensGraphMouse(lensGraphMouse)
|
.lensGraphMouse(lensGraphMouse)
|
||||||
|
@ -249,7 +275,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
MultiSelectedVertexPaintable<AttributedVertex, AttributedEdge> multiSelectedVertexPaintable =
|
MultiSelectedVertexPaintable<AttributedVertex, AttributedEdge> multiSelectedVertexPaintable =
|
||||||
MultiSelectedVertexPaintable.builder(viewer)
|
MultiSelectedVertexPaintable.builder(viewer)
|
||||||
.selectionStrokeMin(4.f)
|
.selectionStrokeMin(4.f)
|
||||||
.selectionPaint(Color.red)
|
.selectionPaint(getSelectedVertexColor())
|
||||||
.useBounds(false)
|
.useBounds(false)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
@ -257,7 +283,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
SingleSelectedVertexPaintable<AttributedVertex, AttributedEdge> singleSelectedVertexPaintable =
|
SingleSelectedVertexPaintable<AttributedVertex, AttributedEdge> singleSelectedVertexPaintable =
|
||||||
SingleSelectedVertexPaintable.builder(viewer)
|
SingleSelectedVertexPaintable.builder(viewer)
|
||||||
.selectionStrokeMin(4.f)
|
.selectionStrokeMin(4.f)
|
||||||
.selectionPaint(Color.red)
|
.selectionPaint(getSelectedVertexColor())
|
||||||
.selectedVertexFunction(vs -> this.focusedVertex)
|
.selectedVertexFunction(vs -> this.focusedVertex)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
@ -375,14 +401,20 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
.popupMenuPath("Go To Edge Source")
|
.popupMenuPath("Go To Edge Source")
|
||||||
.popupMenuGroup("Go To")
|
.popupMenuGroup("Go To")
|
||||||
.withContext(EdgeGraphActionContext.class)
|
.withContext(EdgeGraphActionContext.class)
|
||||||
.onAction(c -> setFocusedVertex(graph.getEdgeSource(c.getClickedEdge())))
|
.onAction(c -> {
|
||||||
|
selectEdge(c.getClickedEdge());
|
||||||
|
setFocusedVertex(graph.getEdgeSource(c.getClickedEdge()));
|
||||||
|
})
|
||||||
.buildAndInstallLocal(componentProvider);
|
.buildAndInstallLocal(componentProvider);
|
||||||
|
|
||||||
new ActionBuilder("Edge Target", ACTION_OWNER)
|
new ActionBuilder("Edge Target", ACTION_OWNER)
|
||||||
.popupMenuPath("Go To Edge Target")
|
.popupMenuPath("Go To Edge Target")
|
||||||
.popupMenuGroup("Go To")
|
.popupMenuGroup("Go To")
|
||||||
.withContext(EdgeGraphActionContext.class)
|
.withContext(EdgeGraphActionContext.class)
|
||||||
.onAction(c -> setFocusedVertex(graph.getEdgeTarget(c.getClickedEdge())))
|
.onAction(c -> {
|
||||||
|
selectEdge(c.getClickedEdge());
|
||||||
|
setFocusedVertex(graph.getEdgeTarget(c.getClickedEdge()));
|
||||||
|
})
|
||||||
.buildAndInstallLocal(componentProvider);
|
.buildAndInstallLocal(componentProvider);
|
||||||
|
|
||||||
hideSelectedAction = new ToggleActionBuilder("Hide Selected", ACTION_OWNER)
|
hideSelectedAction = new ToggleActionBuilder("Hide Selected", ACTION_OWNER)
|
||||||
|
@ -426,6 +458,17 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
.onAction(c -> growSelection(getSourceVerticesFromSelected()))
|
.onAction(c -> growSelection(getSourceVerticesFromSelected()))
|
||||||
.buildAndInstallLocal(componentProvider);
|
.buildAndInstallLocal(componentProvider);
|
||||||
|
|
||||||
|
new ActionBuilder("Grow Selection To Entire Component", ACTION_OWNER)
|
||||||
|
.popupMenuPath("Grow Selection To Entire Component")
|
||||||
|
.popupMenuGroup("z", "4")
|
||||||
|
.description("Extends the current selection by including the target/source vertices " +
|
||||||
|
"of all edges whose source/target is selected")
|
||||||
|
.keyBinding("ctrl C")
|
||||||
|
.enabledWhen(c -> !isAllSelected(getSourceVerticesFromSelected()) && !isAllSelected(getTargetVerticesFromSelected()))
|
||||||
|
.onAction(c -> growSelection(getAllComponentVerticesFromSelected()))
|
||||||
|
.buildAndInstallLocal(componentProvider);
|
||||||
|
|
||||||
|
|
||||||
new ActionBuilder("Clear Selection", ACTION_OWNER)
|
new ActionBuilder("Clear Selection", ACTION_OWNER)
|
||||||
.popupMenuPath("Clear Selection")
|
.popupMenuPath("Clear Selection")
|
||||||
.popupMenuGroup("z", "5")
|
.popupMenuGroup("z", "5")
|
||||||
|
@ -438,7 +481,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
.popupMenuPath("Display Selected as New Graph")
|
.popupMenuPath("Display Selected as New Graph")
|
||||||
.popupMenuGroup("zz", "5")
|
.popupMenuGroup("zz", "5")
|
||||||
.description("Creates a subgraph from the selected nodes")
|
.description("Creates a subgraph from the selected nodes")
|
||||||
.enabledWhen(c -> !viewer.getSelectedVertexState().getSelected().isEmpty())
|
.enabledWhen(c -> !viewer.getSelectedVertices().isEmpty())
|
||||||
.onAction(c -> createAndDisplaySubGraph())
|
.onAction(c -> createAndDisplaySubGraph())
|
||||||
.buildAndInstallLocal(componentProvider);
|
.buildAndInstallLocal(componentProvider);
|
||||||
|
|
||||||
|
@ -505,16 +548,16 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean hasSelection() {
|
private boolean hasSelection() {
|
||||||
return !(viewer.getSelectedVertexState().getSelected().isEmpty() &&
|
return !(viewer.getSelectedVertices().isEmpty() &&
|
||||||
viewer.getSelectedEdgeState().getSelected().isEmpty());
|
viewer.getSelectedEdges().isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isSelected(AttributedVertex v) {
|
private boolean isSelected(AttributedVertex v) {
|
||||||
return viewer.getSelectedVertexState().isSelected(v);
|
return viewer.getSelectedVertices().contains(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isSelected(AttributedEdge e) {
|
private boolean isSelected(AttributedEdge e) {
|
||||||
return viewer.getSelectedEdgeState().isSelected(e);
|
return viewer.getSelectedEdges().contains(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createAndDisplaySubGraph() {
|
private void createAndDisplaySubGraph() {
|
||||||
|
@ -522,6 +565,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
try {
|
try {
|
||||||
display.setGraph(createSubGraph(), "SubGraph", false, TaskMonitor.DUMMY);
|
display.setGraph(createSubGraph(), "SubGraph", false, TaskMonitor.DUMMY);
|
||||||
display.setGraphDisplayListener(listener.cloneWith(display));
|
display.setGraphDisplayListener(listener.cloneWith(display));
|
||||||
|
addedActions.forEach(display::addAction);
|
||||||
}
|
}
|
||||||
catch (CancelledException e) {
|
catch (CancelledException e) {
|
||||||
// using Dummy, so can't happen
|
// using Dummy, so can't happen
|
||||||
|
@ -529,7 +573,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
}
|
}
|
||||||
|
|
||||||
private AttributedGraph createSubGraph() {
|
private AttributedGraph createSubGraph() {
|
||||||
Set<AttributedVertex> selected = viewer.getSelectedVertexState().getSelected();
|
Set<AttributedVertex> selected = viewer.getSelectedVertices();
|
||||||
Graph<AttributedVertex, AttributedEdge> subGraph = new AsSubgraph<>(graph, selected);
|
Graph<AttributedVertex, AttributedEdge> subGraph = new AsSubgraph<>(graph, selected);
|
||||||
|
|
||||||
AttributedGraph newGraph = new AttributedGraph();
|
AttributedGraph newGraph = new AttributedGraph();
|
||||||
|
@ -547,7 +591,23 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isAllSelected(Set<AttributedVertex> vertices) {
|
private boolean isAllSelected(Set<AttributedVertex> vertices) {
|
||||||
return viewer.getSelectedVertexState().getSelected().containsAll(vertices);
|
return viewer.getSelectedVertices().containsAll(vertices);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<AttributedVertex> getSourceVerticesFromSelected() {
|
||||||
|
Set<AttributedVertex> sources = new HashSet<>();
|
||||||
|
Set<AttributedVertex> selectedVertices = getSelectedVertices();
|
||||||
|
selectedVertices.forEach(v -> {
|
||||||
|
Set<AttributedEdge> edges = graph.incomingEdgesOf(v);
|
||||||
|
edges.forEach(e -> sources.add(graph.getEdgeSource(e)));
|
||||||
|
});
|
||||||
|
return sources;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<AttributedVertex> getUnselectedSourceVerticesFromSelected() {
|
||||||
|
MutableSelectedState<AttributedVertex> selectedVertexState = viewer.getSelectedVertexState();
|
||||||
|
return getSourceVerticesFromSelected().stream()
|
||||||
|
.filter(v -> !selectedVertexState.isSelected(v)).collect(Collectors.toSet());
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<AttributedVertex> getTargetVerticesFromSelected() {
|
private Set<AttributedVertex> getTargetVerticesFromSelected() {
|
||||||
|
@ -560,16 +620,43 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
return targets;
|
return targets;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<AttributedVertex> getSourceVerticesFromSelected() {
|
private Set<AttributedVertex> getUnselectedTargetVerticesFromSelected() {
|
||||||
Set<AttributedVertex> sources = new HashSet<>();
|
MutableSelectedState<AttributedVertex> selectedVertexState = viewer.getSelectedVertexState();
|
||||||
Set<AttributedVertex> selectedVertices = getSelectedVertices();
|
return getTargetVerticesFromSelected().stream()
|
||||||
selectedVertices.forEach(v -> {
|
.filter(v -> !selectedVertexState.isSelected(v)).collect(Collectors.toSet());
|
||||||
Set<AttributedEdge> edges = graph.incomingEdgesOf(v);
|
|
||||||
edges.forEach(e -> sources.add(graph.getEdgeSource(e)));
|
|
||||||
});
|
|
||||||
return sources;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Set<AttributedVertex> getAllDownstreamVerticesFromSelected() {
|
||||||
|
MutableSelectedState<AttributedVertex> selectedVertexState = viewer.getSelectedVertexState();
|
||||||
|
Set<AttributedVertex> downstream = new HashSet<>();
|
||||||
|
Set<AttributedVertex> targets = getUnselectedTargetVerticesFromSelected();
|
||||||
|
while (!targets.isEmpty()) {
|
||||||
|
downstream.addAll(targets);
|
||||||
|
growSelection(targets);
|
||||||
|
targets = getUnselectedTargetVerticesFromSelected();
|
||||||
|
}
|
||||||
|
return downstream;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<AttributedVertex> getAllUpstreamVerticesFromSelected() {
|
||||||
|
MutableSelectedState<AttributedVertex> selectedVertexState = viewer.getSelectedVertexState();
|
||||||
|
Set<AttributedVertex> upstream = new HashSet<>();
|
||||||
|
Set<AttributedVertex> sources = getUnselectedSourceVerticesFromSelected();
|
||||||
|
while (!sources.isEmpty()) {
|
||||||
|
growSelection(sources);
|
||||||
|
upstream.addAll(sources);
|
||||||
|
sources = getUnselectedSourceVerticesFromSelected();
|
||||||
|
}
|
||||||
|
return upstream;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<AttributedVertex> getAllComponentVerticesFromSelected() {
|
||||||
|
Set<AttributedVertex> componentVertices = getAllDownstreamVerticesFromSelected();
|
||||||
|
componentVertices.addAll(getAllUpstreamVerticesFromSelected());
|
||||||
|
return componentVertices;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private void invertSelection() {
|
private void invertSelection() {
|
||||||
switchableSelectionListener.setEnabled(false);
|
switchableSelectionListener.setEnabled(false);
|
||||||
try {
|
try {
|
||||||
|
@ -659,11 +746,17 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
SatelliteVisualizationViewer.builder(parentViewer)
|
SatelliteVisualizationViewer.builder(parentViewer)
|
||||||
.viewSize(satelliteSize)
|
.viewSize(satelliteSize)
|
||||||
.build();
|
.build();
|
||||||
satellite.setGraphMouse(new DefaultSatelliteGraphMouse());
|
satellite.setGraphMouse(new DefaultSatelliteGraphMouse<>());
|
||||||
satellite.getRenderContext().setEdgeDrawPaintFunction(Colors::getColor);
|
satellite.getRenderContext().setEdgeDrawPaintFunction(Colors::getColor);
|
||||||
satellite.getRenderContext()
|
satellite.getRenderContext()
|
||||||
.setEdgeStrokeFunction(ProgramGraphFunctions::getEdgeStroke);
|
.setEdgeStrokeFunction(ProgramGraphFunctions::getEdgeStroke);
|
||||||
satellite.getRenderContext().setVertexFillPaintFunction(Colors::getColor);
|
satellite.getRenderContext()
|
||||||
|
.setEdgeDrawPaintFunction(viewer.getRenderContext().getEdgeDrawPaintFunction());
|
||||||
|
satellite.getRenderContext()
|
||||||
|
.setVertexFillPaintFunction(viewer.getRenderContext().getVertexFillPaintFunction());
|
||||||
|
satellite.getRenderContext()
|
||||||
|
.setVertexDrawPaintFunction(viewer.getRenderContext().getVertexDrawPaintFunction());
|
||||||
|
|
||||||
satellite.scaleToLayout();
|
satellite.scaleToLayout();
|
||||||
satellite.getRenderContext().setVertexLabelFunction(n -> null);
|
satellite.getRenderContext().setVertexLabelFunction(n -> null);
|
||||||
// always get the current predicate from the main view and test with it,
|
// always get the current predicate from the main view and test with it,
|
||||||
|
@ -834,14 +927,23 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
// set the graph but defer the layout algorithm setting
|
// set the graph but defer the layout algorithm setting
|
||||||
viewer.getVisualizationModel().setGraph(graph, false);
|
viewer.getVisualizationModel().setGraph(graph, false);
|
||||||
configureFilters();
|
configureFilters();
|
||||||
LayoutAlgorithm<AttributedVertex> initialLayoutAlgorithm =
|
setInitialLayoutAlgorithm();
|
||||||
layoutTransitionManager.getInitialLayoutAlgorithm();
|
|
||||||
initialLayoutAlgorithm.setAfter(() -> centerAndScale());
|
|
||||||
viewer.getVisualizationModel().setLayoutAlgorithm(initialLayoutAlgorithm);
|
|
||||||
});
|
});
|
||||||
componentProvider.setVisible(true);
|
componentProvider.setVisible(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setInitialLayoutAlgorithm() {
|
||||||
|
if (graphDisplayProperties.containsKey("initialLayoutAlgorithm")) {
|
||||||
|
String layoutAlgorithmName = graphDisplayProperties.get("initialLayoutAlgorithm");
|
||||||
|
layoutTransitionManager.setLayout(layoutAlgorithmName);
|
||||||
|
} else {
|
||||||
|
LayoutAlgorithm<AttributedVertex> initialLayoutAlgorithm =
|
||||||
|
layoutTransitionManager.getInitialLayoutAlgorithm();
|
||||||
|
initialLayoutAlgorithm.setAfter(() -> centerAndScale());
|
||||||
|
viewer.getVisualizationModel().setLayoutAlgorithm(initialLayoutAlgorithm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines if a vertex is a root. For our purpose, a root either has no incoming edges
|
* Determines if a vertex is a root. For our purpose, a root either has no incoming edges
|
||||||
* or has at least one outgoing "favored" edge and no incoming "favored" edge
|
* or has at least one outgoing "favored" edge and no incoming "favored" edge
|
||||||
|
@ -935,6 +1037,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
public void setVertexLabel(String attributeName, int alignment, int size, boolean monospace,
|
public void setVertexLabel(String attributeName, int alignment, int size, boolean monospace,
|
||||||
int maxLines) {
|
int maxLines) {
|
||||||
log.fine("setVertexLabel " + attributeName);
|
log.fine("setVertexLabel " + attributeName);
|
||||||
|
this.iconCache.setPreferredLabel(attributeName);
|
||||||
// this would have to set the label function, the label font function
|
// this would have to set the label function, the label font function
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1075,6 +1178,10 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
* @return the new VisualizationViewer
|
* @return the new VisualizationViewer
|
||||||
*/
|
*/
|
||||||
public VisualizationViewer<AttributedVertex, AttributedEdge> createViewer() {
|
public VisualizationViewer<AttributedVertex, AttributedEdge> createViewer() {
|
||||||
|
Color selectedVertexColor =
|
||||||
|
Colors.getHexColor(graphDisplayProperties.getOrDefault("selectedVertexColor", "0xFF0000"));
|
||||||
|
Color selectedEdgeColor =
|
||||||
|
Colors.getHexColor(graphDisplayProperties.getOrDefault("selectedEdgeColor", "0xFF0000"));
|
||||||
final VisualizationViewer<AttributedVertex, AttributedEdge> vv =
|
final VisualizationViewer<AttributedVertex, AttributedEdge> vv =
|
||||||
VisualizationViewer.<AttributedVertex, AttributedEdge> builder()
|
VisualizationViewer.<AttributedVertex, AttributedEdge> builder()
|
||||||
.multiSelectionStrategySupplier(
|
.multiSelectionStrategySupplier(
|
||||||
|
@ -1119,22 +1226,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
this.iconCache = new GhidraIconCache();
|
this.iconCache = new GhidraIconCache();
|
||||||
RenderContext<AttributedVertex, AttributedEdge> renderContext = vv.getRenderContext();
|
RenderContext<AttributedVertex, AttributedEdge> renderContext = vv.getRenderContext();
|
||||||
|
|
||||||
// set up the shape and color functions
|
setVertexPreferences(vv);
|
||||||
IconShapeFunction<AttributedVertex> nodeImageShapeFunction =
|
|
||||||
new IconShapeFunction<>(new EllipseShapeFunction<>());
|
|
||||||
|
|
||||||
vv.getRenderContext().setVertexIconFunction(iconCache::get);
|
|
||||||
|
|
||||||
// cause the vertices to be drawn with custom icons/shapes
|
|
||||||
nodeImageShapeFunction.setIconFunction(iconCache::get);
|
|
||||||
renderContext.setVertexShapeFunction(nodeImageShapeFunction);
|
|
||||||
renderContext.setVertexIconFunction(iconCache::get);
|
|
||||||
|
|
||||||
vv.setInitialDimensionFunction(InitialDimensionFunction
|
|
||||||
.builder(
|
|
||||||
nodeImageShapeFunction.andThen(s -> RectangleUtils.convert(s.getBounds2D())))
|
|
||||||
.build());
|
|
||||||
|
|
||||||
// the selectedEdgeState will be controlled by the vertices that are selected.
|
// the selectedEdgeState will be controlled by the vertices that are selected.
|
||||||
// if both endpoints of an edge are selected, select that edge.
|
// if both endpoints of an edge are selected, select that edge.
|
||||||
vv.setSelectedEdgeState(
|
vv.setSelectedEdgeState(
|
||||||
|
@ -1143,17 +1235,17 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
|
|
||||||
// selected edges will be drawn with a wider stroke
|
// selected edges will be drawn with a wider stroke
|
||||||
renderContext.setEdgeStrokeFunction(
|
renderContext.setEdgeStrokeFunction(
|
||||||
e -> renderContext.getSelectedEdgeState().isSelected(e) ? new BasicStroke(20.f)
|
e -> vv.getSelectedEdges().contains(e) ? new BasicStroke(20.f)
|
||||||
: ProgramGraphFunctions.getEdgeStroke(e));
|
: ProgramGraphFunctions.getEdgeStroke(e));
|
||||||
// selected edges will be drawn in red (instead of default)
|
// selected edges will be drawn in red (instead of default)
|
||||||
renderContext.setEdgeDrawPaintFunction(
|
renderContext.setEdgeDrawPaintFunction(
|
||||||
e -> renderContext.getSelectedEdgeState().isSelected(e) ? Color.red
|
e -> vv.getSelectedEdges().contains(e) ? selectedEdgeColor
|
||||||
: Colors.getColor(e));
|
: Colors.getColor(e));
|
||||||
renderContext.setArrowDrawPaintFunction(
|
renderContext.setArrowDrawPaintFunction(
|
||||||
e -> renderContext.getSelectedEdgeState().isSelected(e) ? Color.red
|
e -> vv.getSelectedEdges().contains(e) ? selectedEdgeColor
|
||||||
: Colors.getColor(e));
|
: Colors.getColor(e));
|
||||||
renderContext.setArrowFillPaintFunction(
|
renderContext.setArrowFillPaintFunction(
|
||||||
e -> renderContext.getSelectedEdgeState().isSelected(e) ? Color.red
|
e -> vv.getSelectedEdges().contains(e) ? selectedEdgeColor
|
||||||
: Colors.getColor(e));
|
: Colors.getColor(e));
|
||||||
|
|
||||||
// assign the shapes to the modal renderer
|
// assign the shapes to the modal renderer
|
||||||
|
@ -1190,6 +1282,35 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
return vv;
|
return vv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setVertexPreferences(VisualizationViewer<AttributedVertex, AttributedEdge> vv) {
|
||||||
|
RenderContext<AttributedVertex, AttributedEdge> renderContext = vv.getRenderContext();
|
||||||
|
if (Boolean.parseBoolean(graphDisplayProperties.getOrDefault("displayVerticesAsIcons", "true"))) {
|
||||||
|
// set up the shape and color functions
|
||||||
|
IconShapeFunction<AttributedVertex> nodeImageShapeFunction =
|
||||||
|
new IconShapeFunction<>(new EllipseShapeFunction<>());
|
||||||
|
|
||||||
|
nodeImageShapeFunction.setIconFunction(iconCache::get);
|
||||||
|
renderContext.setVertexShapeFunction(nodeImageShapeFunction);
|
||||||
|
renderContext.setVertexIconFunction(iconCache::get);
|
||||||
|
|
||||||
|
vv.setInitialDimensionFunction(InitialDimensionFunction
|
||||||
|
.builder(
|
||||||
|
nodeImageShapeFunction.andThen(s -> RectangleUtils.convert(s.getBounds2D())))
|
||||||
|
.build());
|
||||||
|
} else {
|
||||||
|
vv.getRenderContext().setVertexShapeFunction(ProgramGraphFunctions::getVertexShape);
|
||||||
|
vv.setInitialDimensionFunction(InitialDimensionFunction
|
||||||
|
.builder(
|
||||||
|
renderContext.getVertexShapeFunction()
|
||||||
|
.andThen(s -> RectangleUtils.convert(s.getBounds2D())))
|
||||||
|
.build());
|
||||||
|
vv.getRenderContext().setVertexLabelFunction(Object::toString);
|
||||||
|
vv.getRenderContext().setVertexLabelPosition(
|
||||||
|
VertexLabel.Position.valueOf(
|
||||||
|
graphDisplayProperties.getOrDefault("vertexLabelPosition", "AUTO")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Item listener for selection changes in the graph with the additional
|
* Item listener for selection changes in the graph with the additional
|
||||||
* capability of being able to disable the listener without removing it.
|
* capability of being able to disable the listener without removing it.
|
||||||
|
@ -1236,6 +1357,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addAction(DockingAction action) {
|
public void addAction(DockingAction action) {
|
||||||
|
addedActions.add(action);
|
||||||
Swing.runLater(() -> componentProvider.addLocalAction(action));
|
Swing.runLater(() -> componentProvider.addLocalAction(action));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1246,7 +1368,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<AttributedVertex> getSelectedVertices() {
|
public Set<AttributedVertex> getSelectedVertices() {
|
||||||
return viewer.getSelectedVertexState().getSelected();
|
return viewer.getSelectedVertices();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ActionContext getActionContext(MouseEvent e) {
|
public ActionContext getActionContext(MouseEvent e) {
|
||||||
|
@ -1314,6 +1436,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
Swing.runLater(() -> {
|
Swing.runLater(() -> {
|
||||||
// remove all actions
|
// remove all actions
|
||||||
componentProvider.removeAllLocalActions();
|
componentProvider.removeAllLocalActions();
|
||||||
|
addedActions.clear();
|
||||||
// put the standard graph actions back
|
// put the standard graph actions back
|
||||||
createToolbarActions();
|
createToolbarActions();
|
||||||
createPopupActions();
|
createPopupActions();
|
||||||
|
|
|
@ -15,7 +15,9 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.graph.visualization;
|
package ghidra.graph.visualization;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import ghidra.framework.options.Options;
|
import ghidra.framework.options.Options;
|
||||||
|
@ -52,7 +54,12 @@ public class DefaultGraphDisplayProvider implements GraphDisplayProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GraphDisplay getGraphDisplay(boolean reuseGraph,
|
public GraphDisplay getGraphDisplay(boolean reuseGraph, TaskMonitor monitor) {
|
||||||
|
return getGraphDisplay(reuseGraph, Collections.emptyMap(), monitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GraphDisplay getGraphDisplay(boolean reuseGraph, Map<String, String> properties,
|
||||||
TaskMonitor monitor) {
|
TaskMonitor monitor) {
|
||||||
|
|
||||||
if (reuseGraph && !displays.isEmpty()) {
|
if (reuseGraph && !displays.isEmpty()) {
|
||||||
|
@ -62,7 +69,7 @@ public class DefaultGraphDisplayProvider implements GraphDisplayProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
DefaultGraphDisplay display =
|
DefaultGraphDisplay display =
|
||||||
Swing.runNow(() -> new DefaultGraphDisplay(this, displayCounter++));
|
Swing.runNow(() -> new DefaultGraphDisplay(this, properties, displayCounter++));
|
||||||
displays.add(display);
|
displays.add(display);
|
||||||
return display;
|
return display;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ import java.util.*;
|
||||||
|
|
||||||
import org.jungrapht.visualization.VisualizationServer;
|
import org.jungrapht.visualization.VisualizationServer;
|
||||||
import org.jungrapht.visualization.selection.MutableSelectedState;
|
import org.jungrapht.visualization.selection.MutableSelectedState;
|
||||||
import org.jungrapht.visualization.subLayout.VisualGraphCollapser;
|
import org.jungrapht.visualization.sublayout.VisualGraphCollapser;
|
||||||
|
|
||||||
import ghidra.service.graph.AttributedEdge;
|
import ghidra.service.graph.AttributedEdge;
|
||||||
import ghidra.service.graph.AttributedVertex;
|
import ghidra.service.graph.AttributedVertex;
|
||||||
|
@ -31,26 +31,14 @@ import ghidra.service.graph.AttributedVertex;
|
||||||
public class GhidraGraphCollapser extends VisualGraphCollapser<AttributedVertex, AttributedEdge> {
|
public class GhidraGraphCollapser extends VisualGraphCollapser<AttributedVertex, AttributedEdge> {
|
||||||
|
|
||||||
public GhidraGraphCollapser(VisualizationServer<AttributedVertex, AttributedEdge> vv) {
|
public GhidraGraphCollapser(VisualizationServer<AttributedVertex, AttributedEdge> vv) {
|
||||||
super(vv, null);
|
super(vv);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AttributedVertex collapse(Collection<AttributedVertex> selected) {
|
|
||||||
// Unusual Code Alert! - We are forced to set the vertex supplier here
|
|
||||||
// instead of in the constructor because we need the set of vertices that are
|
|
||||||
// going to be grouped at the GroupVertex construction time because it will create
|
|
||||||
// a final id that is based on its contained vertices. A better solution would
|
|
||||||
// be for the super class to take in a vertex factory that can take in the selected
|
|
||||||
// nodes as function parameter when creating the containing GroupVertex.
|
|
||||||
super.setVertexSupplier(() -> GroupVertex.groupVertices(selected));
|
|
||||||
return super.collapse(selected);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ungroups any GroupVertices that are selected
|
* Ungroups any GroupVertices that are selected
|
||||||
*/
|
*/
|
||||||
public void ungroupSelectedVertices() {
|
public void ungroupSelectedVertices() {
|
||||||
expand(vv.getSelectedVertexState().getSelected());
|
expand(vv.getSelectedVertices());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -63,7 +51,7 @@ public class GhidraGraphCollapser extends VisualGraphCollapser<AttributedVertex,
|
||||||
MutableSelectedState<AttributedEdge> selectedEState = vv.getSelectedEdgeState();
|
MutableSelectedState<AttributedEdge> selectedEState = vv.getSelectedEdgeState();
|
||||||
Collection<AttributedVertex> selected = selectedVState.getSelected();
|
Collection<AttributedVertex> selected = selectedVState.getSelected();
|
||||||
if (selected.size() > 1) {
|
if (selected.size() > 1) {
|
||||||
AttributedVertex groupVertex = collapse(selected);
|
AttributedVertex groupVertex = collapse(selected, s -> GroupVertex.groupVertices(selected));
|
||||||
selectedVState.clear();
|
selectedVState.clear();
|
||||||
selectedEState.clear();
|
selectedEState.clear();
|
||||||
selectedVState.select(groupVertex);
|
selectedVState.select(groupVertex);
|
||||||
|
|
|
@ -27,6 +27,7 @@ import javax.swing.border.Border;
|
||||||
import javax.swing.border.CompoundBorder;
|
import javax.swing.border.CompoundBorder;
|
||||||
|
|
||||||
import ghidra.service.graph.AttributedVertex;
|
import ghidra.service.graph.AttributedVertex;
|
||||||
|
import ghidra.service.graph.GraphDisplay;
|
||||||
|
|
||||||
public class GhidraIconCache {
|
public class GhidraIconCache {
|
||||||
|
|
||||||
|
@ -42,6 +43,8 @@ public class GhidraIconCache {
|
||||||
private final Map<AttributedVertex, Icon> map = new ConcurrentHashMap<>();
|
private final Map<AttributedVertex, Icon> map = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
private final IconShape.Function iconShapeFunction = new IconShape.Function();
|
private final IconShape.Function iconShapeFunction = new IconShape.Function();
|
||||||
|
private String preferredLabel = null;
|
||||||
|
private int labelAlignment = GraphDisplay.ALIGN_CENTER;
|
||||||
|
|
||||||
Icon get(AttributedVertex vertex) {
|
Icon get(AttributedVertex vertex) {
|
||||||
|
|
||||||
|
@ -62,7 +65,7 @@ public class GhidraIconCache {
|
||||||
}
|
}
|
||||||
|
|
||||||
private Icon createIcon(AttributedVertex vertex) {
|
private Icon createIcon(AttributedVertex vertex) {
|
||||||
rendererLabel.setText(ProgramGraphFunctions.getLabel(vertex));
|
rendererLabel.setText(ProgramGraphFunctions.getLabel(vertex, preferredLabel));
|
||||||
rendererLabel.setFont(new Font(DEFAULT_FONT_NAME, Font.BOLD, DEFAULT_FONT_SIZE));
|
rendererLabel.setFont(new Font(DEFAULT_FONT_NAME, Font.BOLD, DEFAULT_FONT_SIZE));
|
||||||
rendererLabel.setForeground(Color.black);
|
rendererLabel.setForeground(Color.black);
|
||||||
rendererLabel.setBackground(Color.white);
|
rendererLabel.setBackground(Color.white);
|
||||||
|
@ -193,4 +196,12 @@ public class GhidraIconCache {
|
||||||
public void evict(AttributedVertex vertex) {
|
public void evict(AttributedVertex vertex) {
|
||||||
map.remove(vertex);
|
map.remove(vertex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setPreferredLabel(String attributeName) {
|
||||||
|
this.preferredLabel = attributeName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLabelAlignment(int labelAlignment) {
|
||||||
|
this.labelAlignment = labelAlignment;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -106,8 +106,6 @@ class LayoutTransitionManager {
|
||||||
}
|
}
|
||||||
if (layoutAlgorithm instanceof TreeLayout) {
|
if (layoutAlgorithm instanceof TreeLayout) {
|
||||||
((TreeLayout<AttributedVertex>) layoutAlgorithm).setRootPredicate(rootPredicate);
|
((TreeLayout<AttributedVertex>) layoutAlgorithm).setRootPredicate(rootPredicate);
|
||||||
layoutAlgorithm.setAfter(new PostProcessRunnable<>(
|
|
||||||
visualizationServer.getVisualizationModel().getLayoutModel()));
|
|
||||||
}
|
}
|
||||||
// remove any previously added layout paintables
|
// remove any previously added layout paintables
|
||||||
removePaintable(radialLayoutRings);
|
removePaintable(radialLayoutRings);
|
||||||
|
@ -130,8 +128,7 @@ class LayoutTransitionManager {
|
||||||
((EdgeSorting<AttributedEdge>) layoutAlgorithm).setEdgeComparator(edgeComparator);
|
((EdgeSorting<AttributedEdge>) layoutAlgorithm).setEdgeComparator(edgeComparator);
|
||||||
}
|
}
|
||||||
LayoutAlgorithmTransition.apply(visualizationServer,
|
LayoutAlgorithmTransition.apply(visualizationServer,
|
||||||
layoutAlgorithm,
|
layoutAlgorithm);
|
||||||
new PostProcessRunnable<>(visualizationServer.getVisualizationModel().getLayoutModel()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void removePaintable(VisualizationServer.Paintable paintable) {
|
private void removePaintable(VisualizationServer.Paintable paintable) {
|
||||||
|
@ -150,9 +147,6 @@ class LayoutTransitionManager {
|
||||||
.setRootPredicate(rootPredicate);
|
.setRootPredicate(rootPredicate);
|
||||||
((TreeLayout<AttributedVertex>) initialLayoutAlgorithm)
|
((TreeLayout<AttributedVertex>) initialLayoutAlgorithm)
|
||||||
.setVertexBoundsFunction(vertexBoundsFunction);
|
.setVertexBoundsFunction(vertexBoundsFunction);
|
||||||
initialLayoutAlgorithm.setAfter(new PostProcessRunnable<>(
|
|
||||||
visualizationServer.getVisualizationModel().getLayoutModel()));
|
|
||||||
|
|
||||||
}
|
}
|
||||||
if (initialLayoutAlgorithm instanceof EdgeSorting) {
|
if (initialLayoutAlgorithm instanceof EdgeSorting) {
|
||||||
((EdgeSorting<AttributedEdge>) initialLayoutAlgorithm)
|
((EdgeSorting<AttributedEdge>) initialLayoutAlgorithm)
|
||||||
|
|
|
@ -1,80 +0,0 @@
|
||||||
/* ###
|
|
||||||
* 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.graph.visualization;
|
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
import org.jgrapht.Graph;
|
|
||||||
import org.jungrapht.visualization.layout.model.LayoutModel;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* to post-process tree layouts to move vertices that overlap a vertical edge that
|
|
||||||
* is not incident on the vertex.
|
|
||||||
* This can be removed after jungrapht-layout-1.1
|
|
||||||
* @param <V> vertex type
|
|
||||||
* @param <E> edge type
|
|
||||||
*/
|
|
||||||
public class PostProcessRunnable<V, E> implements Runnable {
|
|
||||||
|
|
||||||
LayoutModel<V> layoutModel;
|
|
||||||
|
|
||||||
public PostProcessRunnable(LayoutModel<V> layoutModel) {
|
|
||||||
this.layoutModel = layoutModel;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
moveVerticesThatOverlapVerticalEdges(layoutModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected int moveVerticesThatOverlapVerticalEdges(LayoutModel<V> layoutModel) {
|
|
||||||
int offset = 100;
|
|
||||||
int moved = 0;
|
|
||||||
Graph<V, E> graph = layoutModel.getGraph();
|
|
||||||
Map<Double, Set<E>> verticalEdgeMap = new LinkedHashMap<>();
|
|
||||||
graph.edgeSet()
|
|
||||||
.stream()
|
|
||||||
.filter(e -> layoutModel.apply(graph.getEdgeSource(e)).x == layoutModel
|
|
||||||
.apply(graph.getEdgeTarget(e)).x)
|
|
||||||
.forEach(e -> verticalEdgeMap
|
|
||||||
.computeIfAbsent(layoutModel.apply(graph.getEdgeSource(e)).x,
|
|
||||||
k -> new HashSet<>())
|
|
||||||
.add(e));
|
|
||||||
|
|
||||||
for (V v : graph.vertexSet()) {
|
|
||||||
double x = layoutModel.apply(v).x;
|
|
||||||
for (E edge : verticalEdgeMap.getOrDefault(x, Collections.emptySet())) {
|
|
||||||
V source = graph.getEdgeSource(edge);
|
|
||||||
V target = graph.getEdgeTarget(edge);
|
|
||||||
if (!v.equals(source) && !v.equals(target)) {
|
|
||||||
double lowy = layoutModel.apply(source).y;
|
|
||||||
double hiy = layoutModel.apply(target).y;
|
|
||||||
if (lowy > hiy) {
|
|
||||||
double temp = lowy;
|
|
||||||
lowy = hiy;
|
|
||||||
hiy = temp;
|
|
||||||
}
|
|
||||||
double vy = layoutModel.apply(v).y;
|
|
||||||
if (lowy <= vy && vy <= hiy) {
|
|
||||||
layoutModel.set(v, layoutModel.apply(v).add(offset, 0));
|
|
||||||
moved++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return moved;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -15,8 +15,6 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.graph.visualization;
|
package ghidra.graph.visualization;
|
||||||
|
|
||||||
import static org.jungrapht.visualization.VisualizationServer.*;
|
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
@ -28,6 +26,8 @@ import com.google.common.base.Splitter;
|
||||||
import ghidra.service.graph.Attributed;
|
import ghidra.service.graph.Attributed;
|
||||||
import ghidra.service.graph.AttributedEdge;
|
import ghidra.service.graph.AttributedEdge;
|
||||||
|
|
||||||
|
import static org.jungrapht.visualization.layout.util.PropertyLoader.PREFIX;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* a container for various functions used by ProgramGraph
|
* a container for various functions used by ProgramGraph
|
||||||
*/
|
*/
|
||||||
|
@ -125,13 +125,14 @@ abstract class ProgramGraphFunctions {
|
||||||
/**
|
/**
|
||||||
* gets a display label from an {@link Attributed} object (vertex)
|
* gets a display label from an {@link Attributed} object (vertex)
|
||||||
* @param attributed the attributed object to get a label for
|
* @param attributed the attributed object to get a label for
|
||||||
|
* @param preferredLabelKey the attribute to use for the label, if available
|
||||||
* @return the label for the given {@link Attributed}
|
* @return the label for the given {@link Attributed}
|
||||||
*/
|
*/
|
||||||
public static String getLabel(Attributed attributed) {
|
public static String getLabel(Attributed attributed, String preferredLabelKey) {
|
||||||
Map<String, String> map = attributed.getAttributeMap();
|
Map<String, String> map = attributed.getAttributeMap();
|
||||||
String name = StringEscapeUtils.escapeHtml4(map.get("Name"));
|
String name = StringEscapeUtils.escapeHtml4(map.get("Name"));
|
||||||
if (map.containsKey("Code")) {
|
if (map.containsKey(preferredLabelKey)) {
|
||||||
name = StringEscapeUtils.escapeHtml4(map.get("Code"));
|
name = StringEscapeUtils.escapeHtml4(map.get(preferredLabelKey));
|
||||||
}
|
}
|
||||||
return "<html>" + String.join("<p>", Splitter.on('\n').split(name));
|
return "<html>" + String.join("<p>", Splitter.on('\n').split(name));
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,14 +45,6 @@ public abstract class AbstractJgtGraphMousePlugin<V, E>
|
||||||
protected V selectedVertex;
|
protected V selectedVertex;
|
||||||
protected E selectedEdge;
|
protected E selectedEdge;
|
||||||
|
|
||||||
public AbstractJgtGraphMousePlugin() {
|
|
||||||
this(InputEvent.BUTTON1_DOWN_MASK);
|
|
||||||
}
|
|
||||||
|
|
||||||
public AbstractJgtGraphMousePlugin(int selectionModifiers) {
|
|
||||||
super(selectionModifiers);
|
|
||||||
}
|
|
||||||
|
|
||||||
public VisualizationViewer<V, E> getViewer(MouseEvent e) {
|
public VisualizationViewer<V, E> getViewer(MouseEvent e) {
|
||||||
VisualizationViewer<V, E> viewer = getGraphViewer(e);
|
VisualizationViewer<V, E> viewer = getGraphViewer(e);
|
||||||
return viewer;
|
return viewer;
|
||||||
|
|
|
@ -33,10 +33,6 @@ import org.jungrapht.visualization.control.AbstractGraphMousePlugin;
|
||||||
public class JgtCursorRestoringPlugin<V, E> extends AbstractGraphMousePlugin
|
public class JgtCursorRestoringPlugin<V, E> extends AbstractGraphMousePlugin
|
||||||
implements MouseMotionListener {
|
implements MouseMotionListener {
|
||||||
|
|
||||||
public JgtCursorRestoringPlugin() {
|
|
||||||
super(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void mouseDragged(MouseEvent e) {
|
public void mouseDragged(MouseEvent e) {
|
||||||
// don't care
|
// don't care
|
||||||
|
|
|
@ -34,10 +34,19 @@ import ghidra.graph.visualization.CenterAnimationJob;
|
||||||
*/
|
*/
|
||||||
public class JgtEdgeNavigationPlugin<V, E> extends AbstractJgtGraphMousePlugin<V, E> {
|
public class JgtEdgeNavigationPlugin<V, E> extends AbstractJgtGraphMousePlugin<V, E> {
|
||||||
|
|
||||||
public JgtEdgeNavigationPlugin() {
|
protected int getSingleSelectionMask;
|
||||||
|
|
||||||
|
public JgtEdgeNavigationPlugin(int singleSelectionMask) {
|
||||||
|
this.singleSelectionMask = singleSelectionMask;
|
||||||
this.cursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR);
|
this.cursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected int singleSelectionMask;
|
||||||
|
|
||||||
|
public boolean checkModifiers(MouseEvent e) {
|
||||||
|
return e.getModifiersEx() == singleSelectionMask;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void mousePressed(MouseEvent e) {
|
public void mousePressed(MouseEvent e) {
|
||||||
if (!checkModifiers(e)) {
|
if (!checkModifiers(e)) {
|
||||||
|
|
|
@ -15,15 +15,14 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.graph.visualization.mouse;
|
package ghidra.graph.visualization.mouse;
|
||||||
|
|
||||||
import java.awt.event.InputEvent;
|
|
||||||
|
|
||||||
import org.jungrapht.visualization.control.*;
|
import org.jungrapht.visualization.control.*;
|
||||||
|
|
||||||
import docking.DockingUtils;
|
|
||||||
import ghidra.graph.visualization.DefaultGraphDisplay;
|
import ghidra.graph.visualization.DefaultGraphDisplay;
|
||||||
import ghidra.service.graph.AttributedEdge;
|
import ghidra.service.graph.AttributedEdge;
|
||||||
import ghidra.service.graph.AttributedVertex;
|
import ghidra.service.graph.AttributedVertex;
|
||||||
|
|
||||||
|
import java.awt.event.InputEvent;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pluggable graph mouse for jungrapht
|
* Pluggable graph mouse for jungrapht
|
||||||
*/
|
*/
|
||||||
|
@ -34,7 +33,7 @@ public class JgtPluggableGraphMouse extends DefaultGraphMouse<AttributedVertex,
|
||||||
// TODO we should not need the graph display for any mouse plugins, but the API is net yet
|
// TODO we should not need the graph display for any mouse plugins, but the API is net yet
|
||||||
// robust enough to communicate fully without it
|
// robust enough to communicate fully without it
|
||||||
public JgtPluggableGraphMouse(DefaultGraphDisplay graphDisplay) {
|
public JgtPluggableGraphMouse(DefaultGraphDisplay graphDisplay) {
|
||||||
super(DefaultGraphMouse.<AttributedVertex, AttributedEdge> builder());
|
super(DefaultGraphMouse.builder());
|
||||||
this.graphDisplay = graphDisplay;
|
this.graphDisplay = graphDisplay;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,23 +46,22 @@ public class JgtPluggableGraphMouse extends DefaultGraphMouse<AttributedVertex,
|
||||||
//
|
//
|
||||||
|
|
||||||
// edge
|
// edge
|
||||||
add(new JgtEdgeNavigationPlugin<AttributedVertex, AttributedEdge>());
|
add(new JgtEdgeNavigationPlugin<>(InputEvent.BUTTON1_DOWN_MASK));
|
||||||
|
|
||||||
add(new JgtVertexFocusingPlugin<AttributedVertex, AttributedEdge>(graphDisplay));
|
add(new JgtVertexFocusingPlugin<>(InputEvent.BUTTON1_DOWN_MASK, graphDisplay));
|
||||||
|
|
||||||
// scaling
|
add(new SelectingGraphMousePlugin<>());
|
||||||
add(new ScalingGraphMousePlugin(new CrossoverScalingControl(), 0, in, out));
|
|
||||||
|
add(new RegionSelectingGraphMousePlugin<>());
|
||||||
|
|
||||||
// the grab/pan feature
|
// the grab/pan feature
|
||||||
add(new JgtTranslatingPlugin<AttributedVertex, AttributedEdge>());
|
add(new TranslatingGraphMousePlugin(InputEvent.BUTTON1_DOWN_MASK));
|
||||||
|
|
||||||
add(new SelectingGraphMousePlugin<AttributedVertex, AttributedEdge>(
|
// scaling
|
||||||
InputEvent.BUTTON1_DOWN_MASK,
|
add(new ScalingGraphMousePlugin());
|
||||||
0,
|
|
||||||
DockingUtils.CONTROL_KEY_MODIFIER_MASK));
|
|
||||||
|
|
||||||
// cursor cleanup
|
// cursor cleanup
|
||||||
add(new JgtCursorRestoringPlugin<AttributedVertex, AttributedEdge>());
|
add(new JgtCursorRestoringPlugin<>());
|
||||||
|
|
||||||
setPluginsLoaded();
|
setPluginsLoaded();
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,16 +40,21 @@ public class JgtTranslatingPlugin<V, E>
|
||||||
|
|
||||||
private boolean panning;
|
private boolean panning;
|
||||||
private boolean isHandlingEvent;
|
private boolean isHandlingEvent;
|
||||||
|
private int translatingMask;
|
||||||
|
|
||||||
public JgtTranslatingPlugin() {
|
public JgtTranslatingPlugin() {
|
||||||
this(InputEvent.BUTTON1_DOWN_MASK);
|
this(InputEvent.BUTTON1_DOWN_MASK);
|
||||||
}
|
}
|
||||||
|
|
||||||
public JgtTranslatingPlugin(int modifiers) {
|
public JgtTranslatingPlugin(int modifiers) {
|
||||||
super(modifiers);
|
this.translatingMask = modifiers;
|
||||||
this.cursor = Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR);
|
this.cursor = Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean checkModifiers(MouseEvent e) {
|
||||||
|
return e.getModifiersEx() == translatingMask;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void mousePressed(MouseEvent e) {
|
public void mousePressed(MouseEvent e) {
|
||||||
boolean accepted = checkModifiers(e) && isInDraggingArea(e);
|
boolean accepted = checkModifiers(e) && isInDraggingArea(e);
|
||||||
|
|
|
@ -15,8 +15,6 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.graph.visualization.mouse;
|
package ghidra.graph.visualization.mouse;
|
||||||
|
|
||||||
import static org.jungrapht.visualization.VisualizationServer.*;
|
|
||||||
|
|
||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
import java.awt.geom.Point2D;
|
import java.awt.geom.Point2D;
|
||||||
import java.awt.geom.Rectangle2D;
|
import java.awt.geom.Rectangle2D;
|
||||||
|
@ -27,6 +25,8 @@ import org.jungrapht.visualization.control.TransformSupport;
|
||||||
import org.jungrapht.visualization.layout.model.LayoutModel;
|
import org.jungrapht.visualization.layout.model.LayoutModel;
|
||||||
import org.jungrapht.visualization.selection.ShapePickSupport;
|
import org.jungrapht.visualization.selection.ShapePickSupport;
|
||||||
|
|
||||||
|
import static org.jungrapht.visualization.layout.util.PropertyLoader.PREFIX;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Keeper of shared logic for jungrapht handling
|
* Keeper of shared logic for jungrapht handling
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -29,11 +29,18 @@ import ghidra.service.graph.AttributedVertex;
|
||||||
public class JgtVertexFocusingPlugin<V, E> extends AbstractJgtGraphMousePlugin<V, E> {
|
public class JgtVertexFocusingPlugin<V, E> extends AbstractJgtGraphMousePlugin<V, E> {
|
||||||
|
|
||||||
private DefaultGraphDisplay graphDisplay;
|
private DefaultGraphDisplay graphDisplay;
|
||||||
|
protected int singleSelectionMask;
|
||||||
|
|
||||||
public JgtVertexFocusingPlugin(DefaultGraphDisplay graphDisplay) {
|
public JgtVertexFocusingPlugin(int singleSelectionMask, DefaultGraphDisplay graphDisplay) {
|
||||||
|
this.singleSelectionMask = singleSelectionMask;
|
||||||
this.graphDisplay = graphDisplay;
|
this.graphDisplay = graphDisplay;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean checkModifiers(MouseEvent e) {
|
||||||
|
return e.getModifiersEx() == singleSelectionMask;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void mousePressed(MouseEvent e) {
|
public void mousePressed(MouseEvent e) {
|
||||||
if (!checkModifiers(e)) {
|
if (!checkModifiers(e)) {
|
||||||
|
|
|
@ -46,9 +46,6 @@ pass
|
||||||
# how many times to iterate over the layers while swapping node positions (mincross)
|
# how many times to iterate over the layers while swapping node positions (mincross)
|
||||||
jungrapht.mincrossTransposeLimit=5
|
jungrapht.mincrossTransposeLimit=5
|
||||||
|
|
||||||
# not using spatial data structures for edges
|
|
||||||
jungrapht.edgeSpatialSupport=NONE
|
|
||||||
|
|
||||||
# over 200 and the eiglsperger algorithm will run instead of the sugiyama
|
# over 200 and the eiglsperger algorithm will run instead of the sugiyama
|
||||||
jungrapht.mincross.eiglspergerThreshold=200
|
jungrapht.mincross.eiglspergerThreshold=200
|
||||||
|
|
||||||
|
@ -63,5 +60,21 @@ jungrapht.initialDimensionVertexDensity=0.3f
|
||||||
jungrapht.minScale=0.001
|
jungrapht.minScale=0.001
|
||||||
jungrapht.maxScale=4.0
|
jungrapht.maxScale=4.0
|
||||||
|
|
||||||
# not using spatial data structures for vertices at this time. May remove after jungrapht 1.1
|
# spatial data structures for vertices
|
||||||
jungrapht.vertexSpatialSupport=NONE
|
jungrapht.vertexSpatialSupport=RTREE
|
||||||
|
# spatial data structures for edges
|
||||||
|
jungrapht.edgeSpatialSupport=RTREE
|
||||||
|
|
||||||
|
#mouse modifiers
|
||||||
|
# the mask for single vertex/edge selection
|
||||||
|
jungrapht.singleSelectionMask=MB1
|
||||||
|
# the mask to augment the selection with a single vertex/edge
|
||||||
|
jungrapht.addSingleSelectionMask=MB1_MENU
|
||||||
|
# the mask to select vertices within a region
|
||||||
|
jungrapht.regionSelectionMask=MB1_MENU
|
||||||
|
# the mask to augment the selection with vertices in a region
|
||||||
|
jungrapht.addRegionSelectionMask=MB1_SHIFT_MENU
|
||||||
|
# the mask to indicate that the selection region is complete/closed and selection may commence
|
||||||
|
jungrapht.regionSelectionCompleteMask=MENU
|
||||||
|
# the mask to indicate that the selection region for augmentation is complete/closed and selection may commence
|
||||||
|
jungrapht.addRegionSelectionCompleteMask=SHIFT_MENU
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.service.graph;
|
package ghidra.service.graph;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import docking.action.DockingAction;
|
import docking.action.DockingAction;
|
||||||
|
@ -33,6 +35,8 @@ public interface GraphDisplay {
|
||||||
public static final int ALIGN_LEFT = 0; // aligns graph text to the left
|
public static final int ALIGN_LEFT = 0; // aligns graph text to the left
|
||||||
public static final int ALIGN_CENTER = 1; // aligns graph text to the center
|
public static final int ALIGN_CENTER = 1; // aligns graph text to the center
|
||||||
public static final int ALIGN_RIGHT = 2; // aligns graph text to the right
|
public static final int ALIGN_RIGHT = 2; // aligns graph text to the right
|
||||||
|
public static final int ALIGN_TOP = 3; // aligns graph text to the right
|
||||||
|
public static final int ALIGN_BOTTOM = 4; // aligns graph text to the right
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets a {@link GraphDisplayListener} to be notified when the user changes the vertex focus
|
* Sets a {@link GraphDisplayListener} to be notified when the user changes the vertex focus
|
||||||
|
@ -108,7 +112,7 @@ public interface GraphDisplay {
|
||||||
/**
|
/**
|
||||||
* Sets the name of the attribute which should be used as the primary vertex label in the display.
|
* Sets the name of the attribute which should be used as the primary vertex label in the display.
|
||||||
* @param attributeName the name of the attribute to use as the display label for vertices.
|
* @param attributeName the name of the attribute to use as the display label for vertices.
|
||||||
* @param alignment (ALIGN_LEFT, ALIGN_RIGHT, or ALIGN_CENTER)
|
* @param alignment (ALIGN_LEFT, ALIGN_RIGHT, ALIGN_CENTER, ALIGN_TOP, or ALIGN_BOTTOM)
|
||||||
* @param size the font size to use for the display label
|
* @param size the font size to use for the display label
|
||||||
* @param monospace true if the font should be monospaced
|
* @param monospace true if the font should be monospaced
|
||||||
* @param maxLines the maximum number lines to display in the vertex labels
|
* @param maxLines the maximum number lines to display in the vertex labels
|
||||||
|
@ -153,4 +157,15 @@ public interface GraphDisplay {
|
||||||
*/
|
*/
|
||||||
public void addAction(DockingAction action);
|
public void addAction(DockingAction action);
|
||||||
|
|
||||||
|
default void setProperty(String key, String value) {
|
||||||
|
}
|
||||||
|
|
||||||
|
default String getValue(String key) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
default Map<String, String> getProperties() {
|
||||||
|
return Collections.emptyMap();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,9 @@ import ghidra.util.classfinder.ExtensionPoint;
|
||||||
import ghidra.util.exception.GraphException;
|
import ghidra.util.exception.GraphException;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Basic interface for objects that can display or otherwise consume a generic graph
|
* Basic interface for objects that can display or otherwise consume a generic graph
|
||||||
*/
|
*/
|
||||||
|
@ -41,8 +44,12 @@ public interface GraphDisplayProvider extends ExtensionPoint {
|
||||||
* @return A GraphDisplay that can be used to display (or otherwise consume - e.g. export) the graph
|
* @return A GraphDisplay that can be used to display (or otherwise consume - e.g. export) the graph
|
||||||
* @throws GraphException thrown if there is a problem creating a GraphDisplay
|
* @throws GraphException thrown if there is a problem creating a GraphDisplay
|
||||||
*/
|
*/
|
||||||
public GraphDisplay getGraphDisplay(boolean reuseGraph,
|
public GraphDisplay getGraphDisplay(boolean reuseGraph, TaskMonitor monitor) throws GraphException;
|
||||||
TaskMonitor monitor) throws GraphException;
|
|
||||||
|
default GraphDisplay getGraphDisplay(boolean reuseGraph, Map<String, String> properties,
|
||||||
|
TaskMonitor monitor) throws GraphException {
|
||||||
|
return getGraphDisplay(reuseGraph, monitor);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides an opportunity for this provider to register and read tool options
|
* Provides an opportunity for this provider to register and read tool options
|
||||||
|
|
|
@ -10,3 +10,5 @@ NOTICE||GHIDRA||||END|
|
||||||
README.md||GHIDRA||||END|
|
README.md||GHIDRA||||END|
|
||||||
build.gradle||GHIDRA||||END|
|
build.gradle||GHIDRA||||END|
|
||||||
settings.gradle||GHIDRA||||END|
|
settings.gradle||GHIDRA||||END|
|
||||||
|
tldr.bat||GHIDRA||||END|
|
||||||
|
tldr.sh||GHIDRA||||END|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue