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
|
||||
public GraphDisplay getDefaultGraphDisplay(boolean reuseGraph,
|
||||
TaskMonitor monitor) throws GraphException {
|
||||
public GraphDisplay getDefaultGraphDisplay(boolean reuseGraph, Map<String, String> properties,
|
||||
TaskMonitor monitor) throws GraphException {
|
||||
if (defaultGraphDisplayProvider != null) {
|
||||
return defaultGraphDisplayProvider.getGraphDisplay(reuseGraph, monitor);
|
||||
return defaultGraphDisplayProvider.getGraphDisplay(reuseGraph, properties, monitor);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -15,7 +15,9 @@
|
|||
*/
|
||||
package ghidra.app.services;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import ghidra.app.plugin.core.graph.GraphDisplayBrokerListener;
|
||||
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.
|
||||
* @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;
|
||||
|
||||
/**
|
||||
|
|
|
@ -30,6 +30,7 @@ import ghidra.program.model.listing.Program;
|
|||
import ghidra.program.model.pcode.*;
|
||||
import ghidra.service.graph.*;
|
||||
import ghidra.util.Msg;
|
||||
import java.util.*;
|
||||
|
||||
public class GraphAST extends GhidraScript {
|
||||
protected static final String COLOR_ATTRIBUTE = "Color";
|
||||
|
@ -64,8 +65,14 @@ public class GraphAST extends GhidraScript {
|
|||
graph = new AttributedGraph();
|
||||
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 =
|
||||
graphDisplayBroker.getDefaultGraphDisplay(false, monitor);
|
||||
graphDisplayBroker.getDefaultGraphDisplay(false, properties, monitor);
|
||||
// graphDisplay.defineVertexAttribute(CODE_ATTRIBUTE); //
|
||||
// graphDisplay.defineVertexAttribute(SYMBOLS_ATTRIBUTE);
|
||||
// graphDisplay.defineEdgeAttribute(EDGE_TYPE_ATTRIBUTE);
|
||||
|
|
|
@ -15,7 +15,9 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.decompile.actions;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
import docking.widgets.EventTrigger;
|
||||
import ghidra.app.services.GraphDisplayBroker;
|
||||
|
@ -101,7 +103,11 @@ public class ASTGraphTask extends Task {
|
|||
else {
|
||||
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 =
|
||||
new ASTGraphDisplayListener(tool, display, hfunction, graphType);
|
||||
display.setGraphDisplayListener(displayListener);
|
||||
|
|
|
@ -11,12 +11,15 @@ eclipse.project.name = 'Features Graph Services'
|
|||
dependencies {
|
||||
compile project(":Base")
|
||||
|
||||
compile "com.github.tomnelson:jungrapht-visualization:1.0"
|
||||
compile "com.github.tomnelson:jungrapht-layout:1.0"
|
||||
|
||||
runtime "org.slf4j:slf4j-api:1.7.25"
|
||||
compile "com.github.tomnelson:jungrapht-visualization:1.1"
|
||||
compile "com.github.tomnelson:jungrapht-layout:1.1"
|
||||
compile "org.jgrapht:jgrapht-core:1.5.0"
|
||||
|
||||
// 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
|
||||
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
|
||||
// runtime "org.apache.logging.log4j:log4j-slf4j-impl:2.12.1"
|
||||
runtime "org.jheaps:jheaps:0.13"
|
||||
|
|
|
@ -87,6 +87,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
|||
|
||||
private Logger log = Logger.getLogger(DefaultGraphDisplay.class.getName());
|
||||
|
||||
private Map<String, String> graphDisplayProperties = new HashMap<>();
|
||||
private GraphDisplayListener listener = new DummyGraphDisplayListener();
|
||||
private String title;
|
||||
|
||||
|
@ -164,15 +165,18 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
|||
private PopupRegulator<AttributedVertex, AttributedEdge> popupRegulator;
|
||||
private GhidraGraphCollapser graphCollapser;
|
||||
|
||||
private Set<DockingAction> addedActions = new LinkedHashSet<>();
|
||||
|
||||
/**
|
||||
* Create the initial display, the graph-less visualization viewer, and its controls
|
||||
* @param displayProvider provides a {@link PluginTool} for Docking features
|
||||
* @param id the unique display id
|
||||
*/
|
||||
DefaultGraphDisplay(DefaultGraphDisplayProvider displayProvider, int id) {
|
||||
DefaultGraphDisplay(DefaultGraphDisplayProvider displayProvider, Map<String, String> graphDisplayProperties, int id) {
|
||||
this.graphDisplayProvider = displayProvider;
|
||||
this.displayId = id;
|
||||
this.pluginTool = graphDisplayProvider.getPluginTool();
|
||||
this.graphDisplayProperties = graphDisplayProperties;
|
||||
this.viewer = createViewer();
|
||||
buildHighlighers();
|
||||
|
||||
|
@ -206,6 +210,28 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
|||
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 component = viewer.getComponent();
|
||||
component.setFocusable(true);
|
||||
|
@ -234,7 +260,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
|||
// this lens' delegate is the viewer's VIEW layer
|
||||
.delegate(transformer)
|
||||
.build();
|
||||
LensGraphMouse lensGraphMouse = new DefaultLensGraphMouse<>(magnificationPlugin);
|
||||
LensGraphMouse lensGraphMouse = DefaultLensGraphMouse.builder().magnificationPlugin(magnificationPlugin).build();
|
||||
return MagnifyImageLensSupport.builder(viewer)
|
||||
.lensTransformer(shapeTransformer)
|
||||
.lensGraphMouse(lensGraphMouse)
|
||||
|
@ -249,7 +275,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
|||
MultiSelectedVertexPaintable<AttributedVertex, AttributedEdge> multiSelectedVertexPaintable =
|
||||
MultiSelectedVertexPaintable.builder(viewer)
|
||||
.selectionStrokeMin(4.f)
|
||||
.selectionPaint(Color.red)
|
||||
.selectionPaint(getSelectedVertexColor())
|
||||
.useBounds(false)
|
||||
.build();
|
||||
|
||||
|
@ -257,7 +283,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
|||
SingleSelectedVertexPaintable<AttributedVertex, AttributedEdge> singleSelectedVertexPaintable =
|
||||
SingleSelectedVertexPaintable.builder(viewer)
|
||||
.selectionStrokeMin(4.f)
|
||||
.selectionPaint(Color.red)
|
||||
.selectionPaint(getSelectedVertexColor())
|
||||
.selectedVertexFunction(vs -> this.focusedVertex)
|
||||
.build();
|
||||
|
||||
|
@ -375,14 +401,20 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
|||
.popupMenuPath("Go To Edge Source")
|
||||
.popupMenuGroup("Go To")
|
||||
.withContext(EdgeGraphActionContext.class)
|
||||
.onAction(c -> setFocusedVertex(graph.getEdgeSource(c.getClickedEdge())))
|
||||
.onAction(c -> {
|
||||
selectEdge(c.getClickedEdge());
|
||||
setFocusedVertex(graph.getEdgeSource(c.getClickedEdge()));
|
||||
})
|
||||
.buildAndInstallLocal(componentProvider);
|
||||
|
||||
new ActionBuilder("Edge Target", ACTION_OWNER)
|
||||
.popupMenuPath("Go To Edge Target")
|
||||
.popupMenuGroup("Go To")
|
||||
.withContext(EdgeGraphActionContext.class)
|
||||
.onAction(c -> setFocusedVertex(graph.getEdgeTarget(c.getClickedEdge())))
|
||||
.onAction(c -> {
|
||||
selectEdge(c.getClickedEdge());
|
||||
setFocusedVertex(graph.getEdgeTarget(c.getClickedEdge()));
|
||||
})
|
||||
.buildAndInstallLocal(componentProvider);
|
||||
|
||||
hideSelectedAction = new ToggleActionBuilder("Hide Selected", ACTION_OWNER)
|
||||
|
@ -426,6 +458,17 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
|||
.onAction(c -> growSelection(getSourceVerticesFromSelected()))
|
||||
.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)
|
||||
.popupMenuPath("Clear Selection")
|
||||
.popupMenuGroup("z", "5")
|
||||
|
@ -438,7 +481,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
|||
.popupMenuPath("Display Selected as New Graph")
|
||||
.popupMenuGroup("zz", "5")
|
||||
.description("Creates a subgraph from the selected nodes")
|
||||
.enabledWhen(c -> !viewer.getSelectedVertexState().getSelected().isEmpty())
|
||||
.enabledWhen(c -> !viewer.getSelectedVertices().isEmpty())
|
||||
.onAction(c -> createAndDisplaySubGraph())
|
||||
.buildAndInstallLocal(componentProvider);
|
||||
|
||||
|
@ -505,16 +548,16 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
|||
}
|
||||
|
||||
private boolean hasSelection() {
|
||||
return !(viewer.getSelectedVertexState().getSelected().isEmpty() &&
|
||||
viewer.getSelectedEdgeState().getSelected().isEmpty());
|
||||
return !(viewer.getSelectedVertices().isEmpty() &&
|
||||
viewer.getSelectedEdges().isEmpty());
|
||||
}
|
||||
|
||||
private boolean isSelected(AttributedVertex v) {
|
||||
return viewer.getSelectedVertexState().isSelected(v);
|
||||
return viewer.getSelectedVertices().contains(v);
|
||||
}
|
||||
|
||||
private boolean isSelected(AttributedEdge e) {
|
||||
return viewer.getSelectedEdgeState().isSelected(e);
|
||||
return viewer.getSelectedEdges().contains(e);
|
||||
}
|
||||
|
||||
private void createAndDisplaySubGraph() {
|
||||
|
@ -522,6 +565,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
|||
try {
|
||||
display.setGraph(createSubGraph(), "SubGraph", false, TaskMonitor.DUMMY);
|
||||
display.setGraphDisplayListener(listener.cloneWith(display));
|
||||
addedActions.forEach(display::addAction);
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
// using Dummy, so can't happen
|
||||
|
@ -529,7 +573,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
|||
}
|
||||
|
||||
private AttributedGraph createSubGraph() {
|
||||
Set<AttributedVertex> selected = viewer.getSelectedVertexState().getSelected();
|
||||
Set<AttributedVertex> selected = viewer.getSelectedVertices();
|
||||
Graph<AttributedVertex, AttributedEdge> subGraph = new AsSubgraph<>(graph, selected);
|
||||
|
||||
AttributedGraph newGraph = new AttributedGraph();
|
||||
|
@ -547,7 +591,23 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
|||
}
|
||||
|
||||
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() {
|
||||
|
@ -560,16 +620,43 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
|||
return targets;
|
||||
}
|
||||
|
||||
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> getUnselectedTargetVerticesFromSelected() {
|
||||
MutableSelectedState<AttributedVertex> selectedVertexState = viewer.getSelectedVertexState();
|
||||
return getTargetVerticesFromSelected().stream()
|
||||
.filter(v -> !selectedVertexState.isSelected(v)).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
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() {
|
||||
switchableSelectionListener.setEnabled(false);
|
||||
try {
|
||||
|
@ -659,11 +746,17 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
|||
SatelliteVisualizationViewer.builder(parentViewer)
|
||||
.viewSize(satelliteSize)
|
||||
.build();
|
||||
satellite.setGraphMouse(new DefaultSatelliteGraphMouse());
|
||||
satellite.setGraphMouse(new DefaultSatelliteGraphMouse<>());
|
||||
satellite.getRenderContext().setEdgeDrawPaintFunction(Colors::getColor);
|
||||
satellite.getRenderContext()
|
||||
.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.getRenderContext().setVertexLabelFunction(n -> null);
|
||||
// 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
|
||||
viewer.getVisualizationModel().setGraph(graph, false);
|
||||
configureFilters();
|
||||
LayoutAlgorithm<AttributedVertex> initialLayoutAlgorithm =
|
||||
layoutTransitionManager.getInitialLayoutAlgorithm();
|
||||
initialLayoutAlgorithm.setAfter(() -> centerAndScale());
|
||||
viewer.getVisualizationModel().setLayoutAlgorithm(initialLayoutAlgorithm);
|
||||
setInitialLayoutAlgorithm();
|
||||
});
|
||||
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
|
||||
* 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,
|
||||
int maxLines) {
|
||||
log.fine("setVertexLabel " + attributeName);
|
||||
this.iconCache.setPreferredLabel(attributeName);
|
||||
// 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
|
||||
*/
|
||||
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 =
|
||||
VisualizationViewer.<AttributedVertex, AttributedEdge> builder()
|
||||
.multiSelectionStrategySupplier(
|
||||
|
@ -1119,22 +1226,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
|||
this.iconCache = new GhidraIconCache();
|
||||
RenderContext<AttributedVertex, AttributedEdge> renderContext = vv.getRenderContext();
|
||||
|
||||
// set up the shape and color functions
|
||||
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());
|
||||
|
||||
setVertexPreferences(vv);
|
||||
// the selectedEdgeState will be controlled by the vertices that are selected.
|
||||
// if both endpoints of an edge are selected, select that edge.
|
||||
vv.setSelectedEdgeState(
|
||||
|
@ -1143,17 +1235,17 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
|||
|
||||
// selected edges will be drawn with a wider stroke
|
||||
renderContext.setEdgeStrokeFunction(
|
||||
e -> renderContext.getSelectedEdgeState().isSelected(e) ? new BasicStroke(20.f)
|
||||
e -> vv.getSelectedEdges().contains(e) ? new BasicStroke(20.f)
|
||||
: ProgramGraphFunctions.getEdgeStroke(e));
|
||||
// selected edges will be drawn in red (instead of default)
|
||||
renderContext.setEdgeDrawPaintFunction(
|
||||
e -> renderContext.getSelectedEdgeState().isSelected(e) ? Color.red
|
||||
e -> vv.getSelectedEdges().contains(e) ? selectedEdgeColor
|
||||
: Colors.getColor(e));
|
||||
renderContext.setArrowDrawPaintFunction(
|
||||
e -> renderContext.getSelectedEdgeState().isSelected(e) ? Color.red
|
||||
e -> vv.getSelectedEdges().contains(e) ? selectedEdgeColor
|
||||
: Colors.getColor(e));
|
||||
renderContext.setArrowFillPaintFunction(
|
||||
e -> renderContext.getSelectedEdgeState().isSelected(e) ? Color.red
|
||||
e -> vv.getSelectedEdges().contains(e) ? selectedEdgeColor
|
||||
: Colors.getColor(e));
|
||||
|
||||
// assign the shapes to the modal renderer
|
||||
|
@ -1190,6 +1282,35 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
|||
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
|
||||
* capability of being able to disable the listener without removing it.
|
||||
|
@ -1236,6 +1357,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
|||
|
||||
@Override
|
||||
public void addAction(DockingAction action) {
|
||||
addedActions.add(action);
|
||||
Swing.runLater(() -> componentProvider.addLocalAction(action));
|
||||
}
|
||||
|
||||
|
@ -1246,7 +1368,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
|||
|
||||
@Override
|
||||
public Set<AttributedVertex> getSelectedVertices() {
|
||||
return viewer.getSelectedVertexState().getSelected();
|
||||
return viewer.getSelectedVertices();
|
||||
}
|
||||
|
||||
public ActionContext getActionContext(MouseEvent e) {
|
||||
|
@ -1314,6 +1436,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
|||
Swing.runLater(() -> {
|
||||
// remove all actions
|
||||
componentProvider.removeAllLocalActions();
|
||||
addedActions.clear();
|
||||
// put the standard graph actions back
|
||||
createToolbarActions();
|
||||
createPopupActions();
|
||||
|
|
|
@ -15,7 +15,9 @@
|
|||
*/
|
||||
package ghidra.graph.visualization;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import ghidra.framework.options.Options;
|
||||
|
@ -52,7 +54,12 @@ public class DefaultGraphDisplayProvider implements GraphDisplayProvider {
|
|||
}
|
||||
|
||||
@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) {
|
||||
|
||||
if (reuseGraph && !displays.isEmpty()) {
|
||||
|
@ -62,7 +69,7 @@ public class DefaultGraphDisplayProvider implements GraphDisplayProvider {
|
|||
}
|
||||
|
||||
DefaultGraphDisplay display =
|
||||
Swing.runNow(() -> new DefaultGraphDisplay(this, displayCounter++));
|
||||
Swing.runNow(() -> new DefaultGraphDisplay(this, properties, displayCounter++));
|
||||
displays.add(display);
|
||||
return display;
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ import java.util.*;
|
|||
|
||||
import org.jungrapht.visualization.VisualizationServer;
|
||||
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.AttributedVertex;
|
||||
|
@ -31,26 +31,14 @@ import ghidra.service.graph.AttributedVertex;
|
|||
public class GhidraGraphCollapser extends VisualGraphCollapser<AttributedVertex, AttributedEdge> {
|
||||
|
||||
public GhidraGraphCollapser(VisualizationServer<AttributedVertex, AttributedEdge> vv) {
|
||||
super(vv, null);
|
||||
}
|
||||
|
||||
@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);
|
||||
super(vv);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ungroups any GroupVertices that are selected
|
||||
*/
|
||||
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();
|
||||
Collection<AttributedVertex> selected = selectedVState.getSelected();
|
||||
if (selected.size() > 1) {
|
||||
AttributedVertex groupVertex = collapse(selected);
|
||||
AttributedVertex groupVertex = collapse(selected, s -> GroupVertex.groupVertices(selected));
|
||||
selectedVState.clear();
|
||||
selectedEState.clear();
|
||||
selectedVState.select(groupVertex);
|
||||
|
|
|
@ -27,6 +27,7 @@ import javax.swing.border.Border;
|
|||
import javax.swing.border.CompoundBorder;
|
||||
|
||||
import ghidra.service.graph.AttributedVertex;
|
||||
import ghidra.service.graph.GraphDisplay;
|
||||
|
||||
public class GhidraIconCache {
|
||||
|
||||
|
@ -42,6 +43,8 @@ public class GhidraIconCache {
|
|||
private final Map<AttributedVertex, Icon> map = new ConcurrentHashMap<>();
|
||||
|
||||
private final IconShape.Function iconShapeFunction = new IconShape.Function();
|
||||
private String preferredLabel = null;
|
||||
private int labelAlignment = GraphDisplay.ALIGN_CENTER;
|
||||
|
||||
Icon get(AttributedVertex vertex) {
|
||||
|
||||
|
@ -62,7 +65,7 @@ public class GhidraIconCache {
|
|||
}
|
||||
|
||||
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.setForeground(Color.black);
|
||||
rendererLabel.setBackground(Color.white);
|
||||
|
@ -193,4 +196,12 @@ public class GhidraIconCache {
|
|||
public void evict(AttributedVertex 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) {
|
||||
((TreeLayout<AttributedVertex>) layoutAlgorithm).setRootPredicate(rootPredicate);
|
||||
layoutAlgorithm.setAfter(new PostProcessRunnable<>(
|
||||
visualizationServer.getVisualizationModel().getLayoutModel()));
|
||||
}
|
||||
// remove any previously added layout paintables
|
||||
removePaintable(radialLayoutRings);
|
||||
|
@ -130,8 +128,7 @@ class LayoutTransitionManager {
|
|||
((EdgeSorting<AttributedEdge>) layoutAlgorithm).setEdgeComparator(edgeComparator);
|
||||
}
|
||||
LayoutAlgorithmTransition.apply(visualizationServer,
|
||||
layoutAlgorithm,
|
||||
new PostProcessRunnable<>(visualizationServer.getVisualizationModel().getLayoutModel()));
|
||||
layoutAlgorithm);
|
||||
}
|
||||
|
||||
private void removePaintable(VisualizationServer.Paintable paintable) {
|
||||
|
@ -150,9 +147,6 @@ class LayoutTransitionManager {
|
|||
.setRootPredicate(rootPredicate);
|
||||
((TreeLayout<AttributedVertex>) initialLayoutAlgorithm)
|
||||
.setVertexBoundsFunction(vertexBoundsFunction);
|
||||
initialLayoutAlgorithm.setAfter(new PostProcessRunnable<>(
|
||||
visualizationServer.getVisualizationModel().getLayoutModel()));
|
||||
|
||||
}
|
||||
if (initialLayoutAlgorithm instanceof EdgeSorting) {
|
||||
((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;
|
||||
|
||||
import static org.jungrapht.visualization.VisualizationServer.*;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.Map;
|
||||
|
||||
|
@ -28,6 +26,8 @@ import com.google.common.base.Splitter;
|
|||
import ghidra.service.graph.Attributed;
|
||||
import ghidra.service.graph.AttributedEdge;
|
||||
|
||||
import static org.jungrapht.visualization.layout.util.PropertyLoader.PREFIX;
|
||||
|
||||
/**
|
||||
* 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)
|
||||
* @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}
|
||||
*/
|
||||
public static String getLabel(Attributed attributed) {
|
||||
public static String getLabel(Attributed attributed, String preferredLabelKey) {
|
||||
Map<String, String> map = attributed.getAttributeMap();
|
||||
String name = StringEscapeUtils.escapeHtml4(map.get("Name"));
|
||||
if (map.containsKey("Code")) {
|
||||
name = StringEscapeUtils.escapeHtml4(map.get("Code"));
|
||||
if (map.containsKey(preferredLabelKey)) {
|
||||
name = StringEscapeUtils.escapeHtml4(map.get(preferredLabelKey));
|
||||
}
|
||||
return "<html>" + String.join("<p>", Splitter.on('\n').split(name));
|
||||
}
|
||||
|
|
|
@ -45,14 +45,6 @@ public abstract class AbstractJgtGraphMousePlugin<V, E>
|
|||
protected V selectedVertex;
|
||||
protected E selectedEdge;
|
||||
|
||||
public AbstractJgtGraphMousePlugin() {
|
||||
this(InputEvent.BUTTON1_DOWN_MASK);
|
||||
}
|
||||
|
||||
public AbstractJgtGraphMousePlugin(int selectionModifiers) {
|
||||
super(selectionModifiers);
|
||||
}
|
||||
|
||||
public VisualizationViewer<V, E> getViewer(MouseEvent e) {
|
||||
VisualizationViewer<V, E> viewer = getGraphViewer(e);
|
||||
return viewer;
|
||||
|
|
|
@ -33,10 +33,6 @@ import org.jungrapht.visualization.control.AbstractGraphMousePlugin;
|
|||
public class JgtCursorRestoringPlugin<V, E> extends AbstractGraphMousePlugin
|
||||
implements MouseMotionListener {
|
||||
|
||||
public JgtCursorRestoringPlugin() {
|
||||
super(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseDragged(MouseEvent e) {
|
||||
// don't care
|
||||
|
|
|
@ -34,10 +34,19 @@ import ghidra.graph.visualization.CenterAnimationJob;
|
|||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
protected int singleSelectionMask;
|
||||
|
||||
public boolean checkModifiers(MouseEvent e) {
|
||||
return e.getModifiersEx() == singleSelectionMask;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mousePressed(MouseEvent e) {
|
||||
if (!checkModifiers(e)) {
|
||||
|
|
|
@ -15,15 +15,14 @@
|
|||
*/
|
||||
package ghidra.graph.visualization.mouse;
|
||||
|
||||
import java.awt.event.InputEvent;
|
||||
|
||||
import org.jungrapht.visualization.control.*;
|
||||
|
||||
import docking.DockingUtils;
|
||||
import ghidra.graph.visualization.DefaultGraphDisplay;
|
||||
import ghidra.service.graph.AttributedEdge;
|
||||
import ghidra.service.graph.AttributedVertex;
|
||||
|
||||
import java.awt.event.InputEvent;
|
||||
|
||||
/**
|
||||
* 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
|
||||
// robust enough to communicate fully without it
|
||||
public JgtPluggableGraphMouse(DefaultGraphDisplay graphDisplay) {
|
||||
super(DefaultGraphMouse.<AttributedVertex, AttributedEdge> builder());
|
||||
super(DefaultGraphMouse.builder());
|
||||
this.graphDisplay = graphDisplay;
|
||||
}
|
||||
|
||||
|
@ -47,23 +46,22 @@ public class JgtPluggableGraphMouse extends DefaultGraphMouse<AttributedVertex,
|
|||
//
|
||||
|
||||
// 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 ScalingGraphMousePlugin(new CrossoverScalingControl(), 0, in, out));
|
||||
add(new SelectingGraphMousePlugin<>());
|
||||
|
||||
add(new RegionSelectingGraphMousePlugin<>());
|
||||
|
||||
// the grab/pan feature
|
||||
add(new JgtTranslatingPlugin<AttributedVertex, AttributedEdge>());
|
||||
add(new TranslatingGraphMousePlugin(InputEvent.BUTTON1_DOWN_MASK));
|
||||
|
||||
add(new SelectingGraphMousePlugin<AttributedVertex, AttributedEdge>(
|
||||
InputEvent.BUTTON1_DOWN_MASK,
|
||||
0,
|
||||
DockingUtils.CONTROL_KEY_MODIFIER_MASK));
|
||||
// scaling
|
||||
add(new ScalingGraphMousePlugin());
|
||||
|
||||
// cursor cleanup
|
||||
add(new JgtCursorRestoringPlugin<AttributedVertex, AttributedEdge>());
|
||||
add(new JgtCursorRestoringPlugin<>());
|
||||
|
||||
setPluginsLoaded();
|
||||
}
|
||||
|
|
|
@ -40,16 +40,21 @@ public class JgtTranslatingPlugin<V, E>
|
|||
|
||||
private boolean panning;
|
||||
private boolean isHandlingEvent;
|
||||
private int translatingMask;
|
||||
|
||||
public JgtTranslatingPlugin() {
|
||||
this(InputEvent.BUTTON1_DOWN_MASK);
|
||||
}
|
||||
|
||||
public JgtTranslatingPlugin(int modifiers) {
|
||||
super(modifiers);
|
||||
this.translatingMask = modifiers;
|
||||
this.cursor = Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR);
|
||||
}
|
||||
|
||||
public boolean checkModifiers(MouseEvent e) {
|
||||
return e.getModifiersEx() == translatingMask;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mousePressed(MouseEvent e) {
|
||||
boolean accepted = checkModifiers(e) && isInDraggingArea(e);
|
||||
|
|
|
@ -15,8 +15,6 @@
|
|||
*/
|
||||
package ghidra.graph.visualization.mouse;
|
||||
|
||||
import static org.jungrapht.visualization.VisualizationServer.*;
|
||||
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.geom.Point2D;
|
||||
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.selection.ShapePickSupport;
|
||||
|
||||
import static org.jungrapht.visualization.layout.util.PropertyLoader.PREFIX;
|
||||
|
||||
/**
|
||||
* 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> {
|
||||
|
||||
private DefaultGraphDisplay graphDisplay;
|
||||
protected int singleSelectionMask;
|
||||
|
||||
public JgtVertexFocusingPlugin(DefaultGraphDisplay graphDisplay) {
|
||||
public JgtVertexFocusingPlugin(int singleSelectionMask, DefaultGraphDisplay graphDisplay) {
|
||||
this.singleSelectionMask = singleSelectionMask;
|
||||
this.graphDisplay = graphDisplay;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkModifiers(MouseEvent e) {
|
||||
return e.getModifiersEx() == singleSelectionMask;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mousePressed(MouseEvent e) {
|
||||
if (!checkModifiers(e)) {
|
||||
|
|
|
@ -46,9 +46,6 @@ pass
|
|||
# how many times to iterate over the layers while swapping node positions (mincross)
|
||||
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
|
||||
jungrapht.mincross.eiglspergerThreshold=200
|
||||
|
||||
|
@ -63,5 +60,21 @@ jungrapht.initialDimensionVertexDensity=0.3f
|
|||
jungrapht.minScale=0.001
|
||||
jungrapht.maxScale=4.0
|
||||
|
||||
# not using spatial data structures for vertices at this time. May remove after jungrapht 1.1
|
||||
jungrapht.vertexSpatialSupport=NONE
|
||||
# spatial data structures for vertices
|
||||
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;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
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_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_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
|
||||
|
@ -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.
|
||||
* @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 monospace true if the font should be monospaced
|
||||
* @param maxLines the maximum number lines to display in the vertex labels
|
||||
|
@ -153,4 +157,15 @@ public interface GraphDisplay {
|
|||
*/
|
||||
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.task.TaskMonitor;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @throws GraphException thrown if there is a problem creating a GraphDisplay
|
||||
*/
|
||||
public GraphDisplay getGraphDisplay(boolean reuseGraph,
|
||||
TaskMonitor monitor) throws GraphException;
|
||||
public GraphDisplay getGraphDisplay(boolean reuseGraph, 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
|
||||
|
|
|
@ -10,3 +10,5 @@ NOTICE||GHIDRA||||END|
|
|||
README.md||GHIDRA||||END|
|
||||
build.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