mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 18:29:37 +02:00
GP-377 - Graphing - A few small refactorings
This commit is contained in:
parent
9f2090d71c
commit
8493c333c8
10 changed files with 112 additions and 130 deletions
|
@ -15,9 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.services;
|
package ghidra.app.services;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.*;
|
||||||
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;
|
||||||
|
@ -62,20 +60,26 @@ 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
|
||||||
*/
|
*/
|
||||||
default GraphDisplay getDefaultGraphDisplay(boolean reuseGraph, TaskMonitor monitor)
|
public default GraphDisplay getDefaultGraphDisplay(boolean reuseGraph, TaskMonitor monitor)
|
||||||
throws GraphException {
|
throws GraphException {
|
||||||
return getDefaultGraphDisplay(reuseGraph, Collections.emptyMap(), monitor);
|
return getDefaultGraphDisplay(reuseGraph, Collections.emptyMap(), monitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A convenience method for getting a {@link GraphDisplay} from the currently active provider
|
* A convenience method for getting a {@link GraphDisplay} from the currently active provider
|
||||||
|
*
|
||||||
|
* <p>This method allows users to override default graph properties defined by
|
||||||
|
* <b>jungrapht</b>. See that library for a complete list of available properties.
|
||||||
|
* Default properties can be changed in the {@code jungrapht.properties} file.
|
||||||
|
*
|
||||||
* @param reuseGraph if true, the provider will attempt to re-use a current graph display
|
* @param reuseGraph if true, the provider will attempt to re-use a current graph display
|
||||||
* @param properties a {@code Map} of property key/values that can be used to customize the display
|
* @param properties a {@code Map} of property key/values that can be used to customize the display
|
||||||
* @param monitor the {@link TaskMonitor} that can be used to cancel the operation
|
* @param monitor the {@link TaskMonitor} that can be used to cancel the operation
|
||||||
* @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
|
||||||
*/
|
*/
|
||||||
GraphDisplay getDefaultGraphDisplay(boolean reuseGraph, Map<String, String> properties, TaskMonitor monitor)
|
public GraphDisplay getDefaultGraphDisplay(boolean reuseGraph, Map<String, String> properties,
|
||||||
|
TaskMonitor monitor)
|
||||||
throws GraphException;
|
throws GraphException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -122,7 +122,7 @@ public class ASTGraphTask extends Task {
|
||||||
display.defineVertexAttribute(CODE_ATTRIBUTE);
|
display.defineVertexAttribute(CODE_ATTRIBUTE);
|
||||||
display.defineVertexAttribute(SYMBOLS_ATTRIBUTE);
|
display.defineVertexAttribute(SYMBOLS_ATTRIBUTE);
|
||||||
|
|
||||||
display.setVertexLabel(CODE_ATTRIBUTE, GraphDisplay.ALIGN_LEFT, 12, true,
|
display.setVertexLabelAttribute(CODE_ATTRIBUTE, GraphDisplay.ALIGN_LEFT, 12, true,
|
||||||
graphType == GraphType.CONTROL_FLOW_GRAPH ? (codeLimitPerBlock + 1) : 1);
|
graphType == GraphType.CONTROL_FLOW_GRAPH ? (codeLimitPerBlock + 1) : 1);
|
||||||
|
|
||||||
String description =
|
String description =
|
||||||
|
|
|
@ -90,7 +90,7 @@ class ExportAttributedGraphDisplay implements GraphDisplay {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setVertexLabel(String attributeName, int alignment, int size, boolean monospace,
|
public void setVertexLabelAttribute(String attributeName, int alignment, int size, boolean monospace,
|
||||||
int maxLines) {
|
int maxLines) {
|
||||||
// no effect
|
// no effect
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,6 +45,7 @@ import org.jungrapht.visualization.layout.model.LayoutModel;
|
||||||
import org.jungrapht.visualization.layout.model.Point;
|
import org.jungrapht.visualization.layout.model.Point;
|
||||||
import org.jungrapht.visualization.renderers.*;
|
import org.jungrapht.visualization.renderers.*;
|
||||||
import org.jungrapht.visualization.renderers.Renderer;
|
import org.jungrapht.visualization.renderers.Renderer;
|
||||||
|
import org.jungrapht.visualization.renderers.Renderer.VertexLabel;
|
||||||
import org.jungrapht.visualization.selection.MutableSelectedState;
|
import org.jungrapht.visualization.selection.MutableSelectedState;
|
||||||
import org.jungrapht.visualization.selection.VertexEndpointsSelectedEdgeSelectedState;
|
import org.jungrapht.visualization.selection.VertexEndpointsSelectedEdgeSelectedState;
|
||||||
import org.jungrapht.visualization.transform.*;
|
import org.jungrapht.visualization.transform.*;
|
||||||
|
@ -64,8 +65,7 @@ import ghidra.framework.plugintool.PluginTool;
|
||||||
import ghidra.graph.AttributeFilters;
|
import ghidra.graph.AttributeFilters;
|
||||||
import ghidra.graph.job.GraphJobRunner;
|
import ghidra.graph.job.GraphJobRunner;
|
||||||
import ghidra.graph.viewer.popup.*;
|
import ghidra.graph.viewer.popup.*;
|
||||||
import ghidra.graph.visualization.mouse.JgtPluggableGraphMouse;
|
import ghidra.graph.visualization.mouse.*;
|
||||||
import ghidra.graph.visualization.mouse.JgtUtils;
|
|
||||||
import ghidra.service.graph.*;
|
import ghidra.service.graph.*;
|
||||||
import ghidra.util.*;
|
import ghidra.util.*;
|
||||||
import ghidra.util.exception.CancelledException;
|
import ghidra.util.exception.CancelledException;
|
||||||
|
@ -82,6 +82,9 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
|
|
||||||
private static final String FAVORED_EDGE = "Fall-Through";
|
private static final String FAVORED_EDGE = "Fall-Through";
|
||||||
|
|
||||||
|
/*
|
||||||
|
A handful of jungrapht properties that re used by this graph
|
||||||
|
*/
|
||||||
private static final String SELECTED_VERTEX_COLOR = "selectedVertexColor";
|
private static final String SELECTED_VERTEX_COLOR = "selectedVertexColor";
|
||||||
private static final String SELECTED_EDGE_COLOR = "selectedEdgeColor";
|
private static final String SELECTED_EDGE_COLOR = "selectedEdgeColor";
|
||||||
private static final String INITIAL_LAYOUT_ALGORITHM = "initialLayoutAlgorithm";
|
private static final String INITIAL_LAYOUT_ALGORITHM = "initialLayoutAlgorithm";
|
||||||
|
@ -94,7 +97,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 Map<String, String> displayProperties = new HashMap<>();
|
||||||
private GraphDisplayListener listener = new DummyGraphDisplayListener();
|
private GraphDisplayListener listener = new DummyGraphDisplayListener();
|
||||||
private String title;
|
private String title;
|
||||||
|
|
||||||
|
@ -162,7 +165,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
/**
|
/**
|
||||||
* Handles all mouse interaction
|
* Handles all mouse interaction
|
||||||
*/
|
*/
|
||||||
private JgtPluggableGraphMouse graphMouse;
|
private JgtGraphMouse graphMouse;
|
||||||
|
|
||||||
private ToggleDockingAction hideSelectedAction;
|
private ToggleDockingAction hideSelectedAction;
|
||||||
private ToggleDockingAction hideUnselectedAction;
|
private ToggleDockingAction hideUnselectedAction;
|
||||||
|
@ -177,13 +180,15 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
/**
|
/**
|
||||||
* 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 displayProperties graph properties that will override the default graph properties
|
||||||
* @param id the unique display id
|
* @param id the unique display id
|
||||||
*/
|
*/
|
||||||
DefaultGraphDisplay(DefaultGraphDisplayProvider displayProvider, Map<String, String> graphDisplayProperties, int id) {
|
DefaultGraphDisplay(DefaultGraphDisplayProvider displayProvider,
|
||||||
|
Map<String, String> displayProperties, 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.displayProperties = displayProperties;
|
||||||
this.viewer = createViewer();
|
this.viewer = createViewer();
|
||||||
buildHighlighers();
|
buildHighlighers();
|
||||||
|
|
||||||
|
@ -217,30 +222,15 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
connectSelectionStateListeners();
|
connectSelectionStateListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setProperty(String key, String value) {
|
|
||||||
this.graphDisplayProperties.put(key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getValue(String key) {
|
|
||||||
return graphDisplayProperties.get(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Map<String, String> getProperties() {
|
|
||||||
return Collections.unmodifiableMap(graphDisplayProperties);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Color getSelectedVertexColor() {
|
private Color getSelectedVertexColor() {
|
||||||
return Colors.getHexColor(graphDisplayProperties.getOrDefault(SELECTED_VERTEX_COLOR,
|
return Colors.getHexColor(displayProperties.getOrDefault(SELECTED_VERTEX_COLOR,
|
||||||
"0xFF0000"));
|
"0xFF0000"));
|
||||||
}
|
|
||||||
|
|
||||||
private Color getSelectedEdgeColor() {
|
|
||||||
return Colors.getHexColor(graphDisplayProperties.getOrDefault(SELECTED_EDGE_COLOR, "0xFF0000"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// private Color getSelectedEdgeColor() {
|
||||||
|
// return Colors
|
||||||
|
// .getHexColor(displayProperties.getOrDefault(SELECTED_EDGE_COLOR, "0xFF0000"));
|
||||||
|
// }
|
||||||
|
|
||||||
JComponent getComponent() {
|
JComponent getComponent() {
|
||||||
JComponent component = viewer.getComponent();
|
JComponent component = viewer.getComponent();
|
||||||
|
@ -270,7 +260,8 @@ 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 = DefaultLensGraphMouse.builder().magnificationPlugin(magnificationPlugin).build();
|
LensGraphMouse lensGraphMouse =
|
||||||
|
DefaultLensGraphMouse.builder().magnificationPlugin(magnificationPlugin).build();
|
||||||
return MagnifyImageLensSupport.builder(viewer)
|
return MagnifyImageLensSupport.builder(viewer)
|
||||||
.lensTransformer(shapeTransformer)
|
.lensTransformer(shapeTransformer)
|
||||||
.lensGraphMouse(lensGraphMouse)
|
.lensGraphMouse(lensGraphMouse)
|
||||||
|
@ -471,14 +462,15 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
new ActionBuilder("Grow Selection To Entire Component", ACTION_OWNER)
|
new ActionBuilder("Grow Selection To Entire Component", ACTION_OWNER)
|
||||||
.popupMenuPath("Grow Selection To Entire Component")
|
.popupMenuPath("Grow Selection To Entire Component")
|
||||||
.popupMenuGroup("z", "4")
|
.popupMenuGroup("z", "4")
|
||||||
.description("Extends the current selection by including the target/source vertices " +
|
.description(
|
||||||
|
"Extends the current selection by including the target/source vertices " +
|
||||||
"of all edges whose source/target is selected")
|
"of all edges whose source/target is selected")
|
||||||
.keyBinding("ctrl C")
|
.keyBinding("ctrl C")
|
||||||
.enabledWhen(c -> !isAllSelected(getSourceVerticesFromSelected()) && !isAllSelected(getTargetVerticesFromSelected()))
|
.enabledWhen(c -> !isAllSelected(getSourceVerticesFromSelected()) &&
|
||||||
|
!isAllSelected(getTargetVerticesFromSelected()))
|
||||||
.onAction(c -> growSelection(getAllComponentVerticesFromSelected()))
|
.onAction(c -> growSelection(getAllComponentVerticesFromSelected()))
|
||||||
.buildAndInstallLocal(componentProvider);
|
.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")
|
||||||
|
@ -615,9 +607,11 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<AttributedVertex> getUnselectedSourceVerticesFromSelected() {
|
private Set<AttributedVertex> getUnselectedSourceVerticesFromSelected() {
|
||||||
MutableSelectedState<AttributedVertex> selectedVertexState = viewer.getSelectedVertexState();
|
MutableSelectedState<AttributedVertex> selectedVertexState =
|
||||||
|
viewer.getSelectedVertexState();
|
||||||
return getSourceVerticesFromSelected().stream()
|
return getSourceVerticesFromSelected().stream()
|
||||||
.filter(v -> !selectedVertexState.isSelected(v)).collect(Collectors.toSet());
|
.filter(v -> !selectedVertexState.isSelected(v))
|
||||||
|
.collect(Collectors.toSet());
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<AttributedVertex> getTargetVerticesFromSelected() {
|
private Set<AttributedVertex> getTargetVerticesFromSelected() {
|
||||||
|
@ -631,13 +625,14 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<AttributedVertex> getUnselectedTargetVerticesFromSelected() {
|
private Set<AttributedVertex> getUnselectedTargetVerticesFromSelected() {
|
||||||
MutableSelectedState<AttributedVertex> selectedVertexState = viewer.getSelectedVertexState();
|
MutableSelectedState<AttributedVertex> selectedVertexState =
|
||||||
|
viewer.getSelectedVertexState();
|
||||||
return getTargetVerticesFromSelected().stream()
|
return getTargetVerticesFromSelected().stream()
|
||||||
.filter(v -> !selectedVertexState.isSelected(v)).collect(Collectors.toSet());
|
.filter(v -> !selectedVertexState.isSelected(v))
|
||||||
|
.collect(Collectors.toSet());
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<AttributedVertex> getAllDownstreamVerticesFromSelected() {
|
private Set<AttributedVertex> getAllDownstreamVerticesFromSelected() {
|
||||||
MutableSelectedState<AttributedVertex> selectedVertexState = viewer.getSelectedVertexState();
|
|
||||||
Set<AttributedVertex> downstream = new HashSet<>();
|
Set<AttributedVertex> downstream = new HashSet<>();
|
||||||
Set<AttributedVertex> targets = getUnselectedTargetVerticesFromSelected();
|
Set<AttributedVertex> targets = getUnselectedTargetVerticesFromSelected();
|
||||||
while (!targets.isEmpty()) {
|
while (!targets.isEmpty()) {
|
||||||
|
@ -649,7 +644,6 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<AttributedVertex> getAllUpstreamVerticesFromSelected() {
|
private Set<AttributedVertex> getAllUpstreamVerticesFromSelected() {
|
||||||
MutableSelectedState<AttributedVertex> selectedVertexState = viewer.getSelectedVertexState();
|
|
||||||
Set<AttributedVertex> upstream = new HashSet<>();
|
Set<AttributedVertex> upstream = new HashSet<>();
|
||||||
Set<AttributedVertex> sources = getUnselectedSourceVerticesFromSelected();
|
Set<AttributedVertex> sources = getUnselectedSourceVerticesFromSelected();
|
||||||
while (!sources.isEmpty()) {
|
while (!sources.isEmpty()) {
|
||||||
|
@ -666,7 +660,6 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
return componentVertices;
|
return componentVertices;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void invertSelection() {
|
private void invertSelection() {
|
||||||
switchableSelectionListener.setEnabled(false);
|
switchableSelectionListener.setEnabled(false);
|
||||||
try {
|
try {
|
||||||
|
@ -756,7 +749,12 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
SatelliteVisualizationViewer.builder(parentViewer)
|
SatelliteVisualizationViewer.builder(parentViewer)
|
||||||
.viewSize(satelliteSize)
|
.viewSize(satelliteSize)
|
||||||
.build();
|
.build();
|
||||||
satellite.setGraphMouse(new DefaultSatelliteGraphMouse<>());
|
|
||||||
|
//
|
||||||
|
// JUNGRAPHT CHANGE 3
|
||||||
|
//
|
||||||
|
satellite.setGraphMouse(new JgtSatelliteGraphMouse());
|
||||||
|
|
||||||
satellite.getRenderContext().setEdgeDrawPaintFunction(Colors::getColor);
|
satellite.getRenderContext().setEdgeDrawPaintFunction(Colors::getColor);
|
||||||
satellite.getRenderContext()
|
satellite.getRenderContext()
|
||||||
.setEdgeStrokeFunction(ProgramGraphFunctions::getEdgeStroke);
|
.setEdgeStrokeFunction(ProgramGraphFunctions::getEdgeStroke);
|
||||||
|
@ -771,9 +769,11 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
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,
|
||||||
satellite.getRenderContext()
|
satellite.getRenderContext()
|
||||||
.setVertexIncludePredicate(v -> viewer.getRenderContext().getVertexIncludePredicate().test(v));
|
.setVertexIncludePredicate(
|
||||||
|
v -> viewer.getRenderContext().getVertexIncludePredicate().test(v));
|
||||||
satellite.getRenderContext()
|
satellite.getRenderContext()
|
||||||
.setEdgeIncludePredicate(e -> viewer.getRenderContext().getEdgeIncludePredicate().test(e));
|
.setEdgeIncludePredicate(
|
||||||
|
e -> viewer.getRenderContext().getEdgeIncludePredicate().test(e));
|
||||||
satellite.getComponent().setBorder(BorderFactory.createEtchedBorder());
|
satellite.getComponent().setBorder(BorderFactory.createEtchedBorder());
|
||||||
parentViewer.getComponent().addComponentListener(new ComponentAdapter() {
|
parentViewer.getComponent().addComponentListener(new ComponentAdapter() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -943,12 +943,13 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setInitialLayoutAlgorithm() {
|
private void setInitialLayoutAlgorithm() {
|
||||||
if (graphDisplayProperties.containsKey(INITIAL_LAYOUT_ALGORITHM)) {
|
if (displayProperties.containsKey(INITIAL_LAYOUT_ALGORITHM)) {
|
||||||
String layoutAlgorithmName = graphDisplayProperties.get(INITIAL_LAYOUT_ALGORITHM);
|
String layoutAlgorithmName = displayProperties.get(INITIAL_LAYOUT_ALGORITHM);
|
||||||
layoutTransitionManager.setLayout(layoutAlgorithmName);
|
layoutTransitionManager.setLayout(layoutAlgorithmName);
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
LayoutAlgorithm<AttributedVertex> initialLayoutAlgorithm =
|
LayoutAlgorithm<AttributedVertex> initialLayoutAlgorithm =
|
||||||
layoutTransitionManager.getInitialLayoutAlgorithm();
|
layoutTransitionManager.getInitialLayoutAlgorithm();
|
||||||
initialLayoutAlgorithm.setAfter(() -> centerAndScale());
|
initialLayoutAlgorithm.setAfter(() -> centerAndScale());
|
||||||
viewer.getVisualizationModel().setLayoutAlgorithm(initialLayoutAlgorithm);
|
viewer.getVisualizationModel().setLayoutAlgorithm(initialLayoutAlgorithm);
|
||||||
}
|
}
|
||||||
|
@ -1040,15 +1041,12 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
log.fine("defineEdgeAttribute " + attributeName + " is not implemented");
|
log.fine("defineEdgeAttribute " + attributeName + " is not implemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* @see ghidra.program.model.graph.GraphDisplay#setVertexLabel(java.lang.String, int, int, boolean, int)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void setVertexLabel(String attributeName, int alignment, int size, boolean monospace,
|
public void setVertexLabelAttribute(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.iconCache.setPreferredVertexLabelAttribute(attributeName);
|
||||||
// this would have to set the label function, the label font function
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1184,15 +1182,11 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* create and return a {@link VisualizationViewer} to display graphs
|
* Create and return a {@link VisualizationViewer} to display graphs
|
||||||
* @return the new VisualizationViewer
|
* @return the new VisualizationViewer
|
||||||
*/
|
*/
|
||||||
public VisualizationViewer<AttributedVertex, AttributedEdge> createViewer() {
|
protected VisualizationViewer<AttributedVertex, AttributedEdge> createViewer() {
|
||||||
Color selectedVertexColor =
|
VisualizationViewer<AttributedVertex, AttributedEdge> vv =
|
||||||
Colors.getHexColor(graphDisplayProperties.getOrDefault("selectedVertexColor", "0xFF0000"));
|
|
||||||
Color selectedEdgeColor =
|
|
||||||
Colors.getHexColor(graphDisplayProperties.getOrDefault("selectedEdgeColor", "0xFF0000"));
|
|
||||||
final VisualizationViewer<AttributedVertex, AttributedEdge> vv =
|
|
||||||
VisualizationViewer.<AttributedVertex, AttributedEdge> builder()
|
VisualizationViewer.<AttributedVertex, AttributedEdge> builder()
|
||||||
.multiSelectionStrategySupplier(
|
.multiSelectionStrategySupplier(
|
||||||
() -> freeFormSelection ? MultiSelectionStrategy.arbitrary()
|
() -> freeFormSelection ? MultiSelectionStrategy.arbitrary()
|
||||||
|
@ -1247,7 +1241,11 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
renderContext.setEdgeStrokeFunction(
|
renderContext.setEdgeStrokeFunction(
|
||||||
e -> vv.getSelectedEdges().contains(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)
|
||||||
|
Color selectedEdgeColor =
|
||||||
|
Colors.getHexColor(
|
||||||
|
displayProperties.getOrDefault("selectedEdgeColor", "0xFF0000"));
|
||||||
renderContext.setEdgeDrawPaintFunction(
|
renderContext.setEdgeDrawPaintFunction(
|
||||||
e -> vv.getSelectedEdges().contains(e) ? selectedEdgeColor
|
e -> vv.getSelectedEdges().contains(e) ? selectedEdgeColor
|
||||||
: Colors.getColor(e));
|
: Colors.getColor(e));
|
||||||
|
@ -1286,7 +1284,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
vv.getComponent().removeMouseListener(mouseListener);
|
vv.getComponent().removeMouseListener(mouseListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
graphMouse = new JgtPluggableGraphMouse(this);
|
graphMouse = new JgtGraphMouse(this);
|
||||||
vv.setGraphMouse(graphMouse);
|
vv.setGraphMouse(graphMouse);
|
||||||
|
|
||||||
return vv;
|
return vv;
|
||||||
|
@ -1294,10 +1292,12 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
|
|
||||||
private void setVertexPreferences(VisualizationViewer<AttributedVertex, AttributedEdge> vv) {
|
private void setVertexPreferences(VisualizationViewer<AttributedVertex, AttributedEdge> vv) {
|
||||||
RenderContext<AttributedVertex, AttributedEdge> renderContext = vv.getRenderContext();
|
RenderContext<AttributedVertex, AttributedEdge> renderContext = vv.getRenderContext();
|
||||||
if (Boolean.parseBoolean(graphDisplayProperties.getOrDefault(DISPLAY_VERTICES_AS_ICONS, "true"))) {
|
String useIcons =
|
||||||
|
displayProperties.getOrDefault(DISPLAY_VERTICES_AS_ICONS, Boolean.TRUE.toString());
|
||||||
|
if (Boolean.parseBoolean(useIcons)) {
|
||||||
// set up the shape and color functions
|
// set up the shape and color functions
|
||||||
IconShapeFunction<AttributedVertex> nodeImageShapeFunction =
|
IconShapeFunction<AttributedVertex> nodeImageShapeFunction =
|
||||||
new IconShapeFunction<>(new EllipseShapeFunction<>());
|
new IconShapeFunction<>(new EllipseShapeFunction<>());
|
||||||
|
|
||||||
nodeImageShapeFunction.setIconFunction(iconCache::get);
|
nodeImageShapeFunction.setIconFunction(iconCache::get);
|
||||||
renderContext.setVertexShapeFunction(nodeImageShapeFunction);
|
renderContext.setVertexShapeFunction(nodeImageShapeFunction);
|
||||||
|
@ -1305,19 +1305,22 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
|
|
||||||
vv.setInitialDimensionFunction(InitialDimensionFunction
|
vv.setInitialDimensionFunction(InitialDimensionFunction
|
||||||
.builder(
|
.builder(
|
||||||
nodeImageShapeFunction.andThen(s -> RectangleUtils.convert(s.getBounds2D())))
|
nodeImageShapeFunction
|
||||||
|
.andThen(s -> RectangleUtils.convert(s.getBounds2D())))
|
||||||
.build());
|
.build());
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
vv.getRenderContext().setVertexShapeFunction(ProgramGraphFunctions::getVertexShape);
|
vv.getRenderContext().setVertexShapeFunction(ProgramGraphFunctions::getVertexShape);
|
||||||
vv.setInitialDimensionFunction(InitialDimensionFunction
|
vv.setInitialDimensionFunction(InitialDimensionFunction
|
||||||
.builder(
|
.builder(
|
||||||
renderContext.getVertexShapeFunction()
|
renderContext.getVertexShapeFunction()
|
||||||
.andThen(s -> RectangleUtils.convert(s.getBounds2D())))
|
.andThen(s -> RectangleUtils.convert(s.getBounds2D())))
|
||||||
.build());
|
.build());
|
||||||
vv.getRenderContext().setVertexLabelFunction(Object::toString);
|
vv.getRenderContext().setVertexLabelFunction(Object::toString);
|
||||||
vv.getRenderContext().setVertexLabelPosition(
|
vv.getRenderContext()
|
||||||
VertexLabel.Position.valueOf(
|
.setVertexLabelPosition(
|
||||||
graphDisplayProperties.getOrDefault(VERTEX_LABEL_POSITION, "AUTO")));
|
VertexLabel.Position.valueOf(
|
||||||
|
displayProperties.getOrDefault(VERTEX_LABEL_POSITION, "AUTO")));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,6 @@ 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 {
|
||||||
|
|
||||||
|
@ -43,8 +42,7 @@ 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 String preferredVeretxLabelAttribute = null;
|
||||||
private int labelAlignment = GraphDisplay.ALIGN_CENTER;
|
|
||||||
|
|
||||||
Icon get(AttributedVertex vertex) {
|
Icon get(AttributedVertex vertex) {
|
||||||
|
|
||||||
|
@ -65,7 +63,8 @@ public class GhidraIconCache {
|
||||||
}
|
}
|
||||||
|
|
||||||
private Icon createIcon(AttributedVertex vertex) {
|
private Icon createIcon(AttributedVertex vertex) {
|
||||||
rendererLabel.setText(ProgramGraphFunctions.getLabel(vertex, preferredLabel));
|
rendererLabel
|
||||||
|
.setText(ProgramGraphFunctions.getLabel(vertex, preferredVeretxLabelAttribute));
|
||||||
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);
|
||||||
|
@ -103,17 +102,21 @@ public class GhidraIconCache {
|
||||||
// triangles have a non-zero +/- yoffset instead of centering the label
|
// triangles have a non-zero +/- yoffset instead of centering the label
|
||||||
case TRIANGLE:
|
case TRIANGLE:
|
||||||
// scale the vertex shape
|
// scale the vertex shape
|
||||||
scalex = labelSize.getWidth() / vertexShape.getBounds().getWidth() * LABEL_TO_ICON_PROPORTION;
|
scalex = labelSize.getWidth() / vertexShape.getBounds().getWidth() *
|
||||||
scaley = labelSize.getHeight() / vertexShape.getBounds().getHeight() * LABEL_TO_ICON_PROPORTION;
|
LABEL_TO_ICON_PROPORTION;
|
||||||
|
scaley = labelSize.getHeight() / vertexShape.getBounds().getHeight() *
|
||||||
|
LABEL_TO_ICON_PROPORTION;
|
||||||
vertexShape = AffineTransform.getScaleInstance(scalex, scaley)
|
vertexShape = AffineTransform.getScaleInstance(scalex, scaley)
|
||||||
.createTransformedShape(vertexShape);
|
.createTransformedShape(vertexShape);
|
||||||
offset = -(int) ((vertexShape.getBounds().getHeight() - labelSize.getHeight()) / 2);
|
offset = -(int) ((vertexShape.getBounds().getHeight() - labelSize.getHeight()) / 2);
|
||||||
break;
|
break;
|
||||||
case INVERTED_TRIANGLE:
|
case INVERTED_TRIANGLE:
|
||||||
scalex = labelSize.getWidth() / vertexShape.getBounds().getWidth() * LABEL_TO_ICON_PROPORTION;
|
scalex = labelSize.getWidth() / vertexShape.getBounds().getWidth() *
|
||||||
scaley = labelSize.getHeight() / vertexShape.getBounds().getHeight() * LABEL_TO_ICON_PROPORTION;
|
LABEL_TO_ICON_PROPORTION;
|
||||||
|
scaley = labelSize.getHeight() / vertexShape.getBounds().getHeight() *
|
||||||
|
LABEL_TO_ICON_PROPORTION;
|
||||||
vertexShape = AffineTransform.getScaleInstance(scalex, scaley)
|
vertexShape = AffineTransform.getScaleInstance(scalex, scaley)
|
||||||
.createTransformedShape(vertexShape);
|
.createTransformedShape(vertexShape);
|
||||||
offset = (int) ((vertexShape.getBounds().getHeight() - labelSize.getHeight()) / 2);
|
offset = (int) ((vertexShape.getBounds().getHeight() - labelSize.getHeight()) / 2);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -122,17 +125,17 @@ public class GhidraIconCache {
|
||||||
scalex = labelSize.getWidth() / vertexShape.getBounds().getWidth();
|
scalex = labelSize.getWidth() / vertexShape.getBounds().getWidth();
|
||||||
scaley = labelSize.getHeight() / vertexShape.getBounds().getHeight();
|
scaley = labelSize.getHeight() / vertexShape.getBounds().getHeight();
|
||||||
vertexShape = AffineTransform.getScaleInstance(scalex, scaley)
|
vertexShape = AffineTransform.getScaleInstance(scalex, scaley)
|
||||||
.createTransformedShape(vertexShape);
|
.createTransformedShape(vertexShape);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// diamonds and ellipses reduce the label size to fit
|
// diamonds and ellipses reduce the label size to fit
|
||||||
case DIAMOND:
|
case DIAMOND:
|
||||||
default: // ELLIPSE
|
default: // ELLIPSE
|
||||||
scalex =
|
scalex =
|
||||||
labelSize.getWidth() / vertexShape.getBounds().getWidth() * 1.1;
|
labelSize.getWidth() / vertexShape.getBounds().getWidth() * 1.1;
|
||||||
scaley = labelSize.getHeight() / vertexShape.getBounds().getHeight() * 1.1;
|
scaley = labelSize.getHeight() / vertexShape.getBounds().getHeight() * 1.1;
|
||||||
vertexShape = AffineTransform.getScaleInstance(scalex, scaley)
|
vertexShape = AffineTransform.getScaleInstance(scalex, scaley)
|
||||||
.createTransformedShape(vertexShape);
|
.createTransformedShape(vertexShape);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Rectangle vertexBounds = vertexShape.getBounds();
|
Rectangle vertexBounds = vertexShape.getBounds();
|
||||||
|
@ -166,13 +169,13 @@ public class GhidraIconCache {
|
||||||
label.paint(graphics);
|
label.paint(graphics);
|
||||||
// draw the shape again, but lighter (on top of the label)
|
// draw the shape again, but lighter (on top of the label)
|
||||||
offsetTransform =
|
offsetTransform =
|
||||||
AffineTransform.getTranslateInstance(strokeThickness + vertexBounds.width / 2.0,
|
AffineTransform.getTranslateInstance(strokeThickness + vertexBounds.width / 2.0,
|
||||||
strokeThickness + vertexBounds.height / 2.0);
|
strokeThickness + vertexBounds.height / 2.0);
|
||||||
offsetTransform.preConcatenate(graphicsTransform);
|
offsetTransform.preConcatenate(graphicsTransform);
|
||||||
graphics.setTransform(offsetTransform);
|
graphics.setTransform(offsetTransform);
|
||||||
Paint paint = Colors.getColor(vertex);
|
Paint paint = Colors.getColor(vertex);
|
||||||
if (paint instanceof Color) {
|
if (paint instanceof Color) {
|
||||||
Color color = (Color)paint;
|
Color color = (Color) paint;
|
||||||
Color transparent = new Color(color.getRed(), color.getGreen(), color.getBlue(), 50);
|
Color transparent = new Color(color.getRed(), color.getGreen(), color.getBlue(), 50);
|
||||||
graphics.setPaint(transparent);
|
graphics.setPaint(transparent);
|
||||||
graphics.setStroke(new BasicStroke(strokeThickness));
|
graphics.setStroke(new BasicStroke(strokeThickness));
|
||||||
|
@ -201,7 +204,7 @@ public class GhidraIconCache {
|
||||||
* Sets the vertex label to the value of the passed attribute name
|
* Sets the vertex label to the value of the passed attribute name
|
||||||
* @param attributeName the attribute key for the vertex label value to be displayed
|
* @param attributeName the attribute key for the vertex label value to be displayed
|
||||||
*/
|
*/
|
||||||
public void setPreferredLabel(String attributeName) {
|
public void setPreferredVertexLabelAttribute(String attributeName) {
|
||||||
this.preferredLabel = attributeName;
|
this.preferredVeretxLabelAttribute = attributeName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.graph.visualization;
|
package ghidra.graph.visualization;
|
||||||
|
|
||||||
|
import static org.jungrapht.visualization.layout.util.PropertyLoader.*;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
@ -26,8 +28,6 @@ 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,14 +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
|
* @param preferredLabelAttribute 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, String preferredLabelKey) {
|
public static String getLabel(Attributed attributed, String preferredLabelAttribute) {
|
||||||
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(preferredLabelKey)) {
|
if (map.containsKey(preferredLabelAttribute)) {
|
||||||
name = StringEscapeUtils.escapeHtml4(map.get(preferredLabelKey));
|
name = StringEscapeUtils.escapeHtml4(map.get(preferredLabelAttribute));
|
||||||
}
|
}
|
||||||
return "<html>" + String.join("<p>", Splitter.on('\n').split(name));
|
return "<html>" + String.join("<p>", Splitter.on('\n').split(name));
|
||||||
}
|
}
|
||||||
|
|
|
@ -150,7 +150,7 @@ public class BlockGraphTask extends Task {
|
||||||
if (showCode) {
|
if (showCode) {
|
||||||
display.defineVertexAttribute(CODE_ATTRIBUTE);
|
display.defineVertexAttribute(CODE_ATTRIBUTE);
|
||||||
display.defineVertexAttribute(SYMBOLS_ATTRIBUTE);
|
display.defineVertexAttribute(SYMBOLS_ATTRIBUTE);
|
||||||
display.setVertexLabel(CODE_ATTRIBUTE, GraphDisplay.ALIGN_LEFT, 12, true,
|
display.setVertexLabelAttribute(CODE_ATTRIBUTE, GraphDisplay.ALIGN_LEFT, 12, true,
|
||||||
codeLimitPerBlock + 1);
|
codeLimitPerBlock + 1);
|
||||||
}
|
}
|
||||||
display.setGraph(graph, graphTitle, appendGraph, monitor);
|
display.setGraph(graph, graphTitle, appendGraph, monitor);
|
||||||
|
|
|
@ -74,7 +74,7 @@ public class TestGraphDisplay implements GraphDisplay {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setVertexLabel(String attributeName, int alignment, int size, boolean monospace,
|
public void setVertexLabelAttribute(String attributeName, int alignment, int size, boolean monospace,
|
||||||
int maxLines) {
|
int maxLines) {
|
||||||
// nothing
|
// nothing
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,8 +15,6 @@
|
||||||
*/
|
*/
|
||||||
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;
|
||||||
|
@ -115,7 +113,7 @@ public interface GraphDisplay {
|
||||||
* @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
|
||||||
*/
|
*/
|
||||||
public void setVertexLabel(String attributeName, int alignment, int size, boolean monospace,
|
public void setVertexLabelAttribute(String attributeName, int alignment, int size, boolean monospace,
|
||||||
int maxLines);
|
int maxLines);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -154,30 +152,4 @@ public interface GraphDisplay {
|
||||||
* @param action the action to add.
|
* @param action the action to add.
|
||||||
*/
|
*/
|
||||||
public void addAction(DockingAction action);
|
public void addAction(DockingAction action);
|
||||||
|
|
||||||
/**
|
|
||||||
* set a property key/value pair. This may be used to pass preferences to the implementation
|
|
||||||
* @param key the property key
|
|
||||||
* @param value the propery value
|
|
||||||
*/
|
|
||||||
default void setProperty(String key, String value) {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param key the key to fetch a value for
|
|
||||||
* @return the value associated with the passed key
|
|
||||||
*/
|
|
||||||
default String getValue(String key) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the property {@code Map} Should be implemented to pass an unmodifiable or copy
|
|
||||||
* @return the complete {@code Map} of properties.
|
|
||||||
*/
|
|
||||||
default Map<String, String> getProperties() {
|
|
||||||
return Collections.emptyMap();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue