GP-377 - Graphing - A few small refactorings

This commit is contained in:
dragonmacher 2021-02-02 15:41:48 -05:00
parent 9f2090d71c
commit 8493c333c8
10 changed files with 112 additions and 130 deletions

View file

@ -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;
/** /**

View file

@ -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 =

View file

@ -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
} }

View file

@ -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() { // private Color getSelectedEdgeColor() {
return Colors.getHexColor(graphDisplayProperties.getOrDefault(SELECTED_EDGE_COLOR, "0xFF0000")); // 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,10 +943,11 @@ 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());
@ -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,7 +1292,9 @@ 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<>());
@ -1305,9 +1305,11 @@ 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(
@ -1315,9 +1317,10 @@ public class DefaultGraphDisplay implements GraphDisplay {
.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()
.setVertexLabelPosition(
VertexLabel.Position.valueOf( VertexLabel.Position.valueOf(
graphDisplayProperties.getOrDefault(VERTEX_LABEL_POSITION, "AUTO"))); displayProperties.getOrDefault(VERTEX_LABEL_POSITION, "AUTO")));
} }
} }

View file

@ -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,15 +102,19 @@ 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);
@ -172,7 +175,7 @@ public class GhidraIconCache {
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;
} }
} }

View file

@ -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));
} }

View file

@ -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);

View file

@ -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
} }

View file

@ -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();
}
} }