mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 10:49:34 +02:00
Graphing - (various fixes; see below)
-fixed bug that prevented actions being added to sub graphs -subgraph gets bad tab text -properties documentation -fixed incorrect vertex shape function applied to lightweight renderer -dispose actions on close
This commit is contained in:
parent
4041c3693b
commit
e983784753
10 changed files with 262 additions and 179 deletions
|
@ -68,9 +68,8 @@ public interface GraphDisplayBroker {
|
|||
/**
|
||||
* 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.
|
||||
* <p>This method allows users to override default graph properties for the graph provider
|
||||
* being created. See the graph provider implementation for a list of supported properties
|
||||
*
|
||||
* @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
|
||||
|
|
|
@ -19,7 +19,7 @@ import java.util.*;
|
|||
|
||||
import org.jgrapht.Graph;
|
||||
|
||||
import docking.action.DockingAction;
|
||||
import docking.action.DockingActionIf;
|
||||
import docking.widgets.EventTrigger;
|
||||
import ghidra.app.services.GraphDisplayBroker;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
|
@ -59,8 +59,6 @@ class ExportAttributedGraphDisplay implements GraphDisplay {
|
|||
listener.dispose();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* set the {@link AttributedGraph} for visualization
|
||||
* @param attributedGraph the {@link AttributedGraph} to visualize
|
||||
|
@ -90,7 +88,8 @@ class ExportAttributedGraphDisplay implements GraphDisplay {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void setVertexLabelAttribute(String attributeName, int alignment, int size, boolean monospace,
|
||||
public void setVertexLabelAttribute(String attributeName, int alignment, int size,
|
||||
boolean monospace,
|
||||
int maxLines) {
|
||||
// no effect
|
||||
}
|
||||
|
@ -122,7 +121,7 @@ class ExportAttributedGraphDisplay implements GraphDisplay {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void addAction(DockingAction action) {
|
||||
public void addAction(DockingActionIf action) {
|
||||
// do nothing, actions are not supported by this display
|
||||
}
|
||||
|
||||
|
|
|
@ -19,10 +19,12 @@ import static org.jungrapht.visualization.MultiLayerTransformer.Layer.*;
|
|||
import static org.jungrapht.visualization.renderers.BiModalRenderer.*;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.event.*;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.stream.Collectors;
|
||||
|
@ -43,9 +45,11 @@ import org.jungrapht.visualization.layout.algorithms.LayoutAlgorithm;
|
|||
import org.jungrapht.visualization.layout.algorithms.util.InitialDimensionFunction;
|
||||
import org.jungrapht.visualization.layout.model.LayoutModel;
|
||||
import org.jungrapht.visualization.layout.model.Point;
|
||||
import org.jungrapht.visualization.layout.model.Rectangle;
|
||||
import org.jungrapht.visualization.renderers.*;
|
||||
import org.jungrapht.visualization.renderers.Renderer;
|
||||
import org.jungrapht.visualization.renderers.Renderer.VertexLabel;
|
||||
import org.jungrapht.visualization.renderers.Renderer.VertexLabel.Position;
|
||||
import org.jungrapht.visualization.selection.MutableSelectedState;
|
||||
import org.jungrapht.visualization.selection.VertexEndpointsSelectedEdgeSelectedState;
|
||||
import org.jungrapht.visualization.transform.*;
|
||||
|
@ -54,7 +58,8 @@ import org.jungrapht.visualization.transform.shape.MagnifyShapeTransformer;
|
|||
import org.jungrapht.visualization.util.RectangleUtils;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.action.DockingAction;
|
||||
import docking.DockingActionProxy;
|
||||
import docking.action.DockingActionIf;
|
||||
import docking.action.ToggleDockingAction;
|
||||
import docking.action.builder.*;
|
||||
import docking.menu.ActionState;
|
||||
|
@ -75,6 +80,24 @@ import resources.Icons;
|
|||
|
||||
/**
|
||||
* Delegates to a {@link VisualizationViewer} to draw a graph visualization
|
||||
*
|
||||
* <P>This graph uses the following properties:
|
||||
* <UL>
|
||||
* <LI>selectedVertexColor - hex color using '0x' or '#', with 6 digits
|
||||
* </LI>
|
||||
* <LI>selectedEdgeColor - hex color using '0x' or '#', with 6 digits
|
||||
* </LI>
|
||||
* <LI>displayVerticesAsIcons - if true, shapes will be used to draw vertices based upon
|
||||
* {@link GhidraIconCache}; false, then vertex shapes will be created from
|
||||
* {@link ProgramGraphFunctions#getVertexShape(Attributed)}
|
||||
* </LI>
|
||||
* <LI>vertexLabelPosition - see {@link Position}
|
||||
* </LI>
|
||||
* <LI>initialLayoutAlgorithm - the name of the layout algorithm to be used for the initial
|
||||
* graph layout
|
||||
* </LI>
|
||||
* </UL>
|
||||
*
|
||||
*/
|
||||
public class DefaultGraphDisplay implements GraphDisplay {
|
||||
|
||||
|
@ -83,7 +106,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
|||
private static final String FAVORED_EDGE = "Fall-Through";
|
||||
|
||||
/*
|
||||
A handful of jungrapht properties that re used by this graph
|
||||
A handful of properties that can be set via the constructor
|
||||
*/
|
||||
private static final String SELECTED_VERTEX_COLOR = "selectedVertexColor";
|
||||
private static final String SELECTED_EDGE_COLOR = "selectedEdgeColor";
|
||||
|
@ -98,6 +121,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
|||
private Logger log = Logger.getLogger(DefaultGraphDisplay.class.getName());
|
||||
|
||||
private Map<String, String> displayProperties = new HashMap<>();
|
||||
private Set<DockingActionIf> addedActions = new LinkedHashSet<>();
|
||||
private GraphDisplayListener listener = new DummyGraphDisplayListener();
|
||||
private String title;
|
||||
|
||||
|
@ -175,8 +199,6 @@ 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
|
||||
|
@ -223,14 +245,14 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
|||
}
|
||||
|
||||
private Color getSelectedVertexColor() {
|
||||
return Colors.getHexColor(displayProperties.getOrDefault(SELECTED_VERTEX_COLOR,
|
||||
"0xFF0000"));
|
||||
String property = displayProperties.getOrDefault(SELECTED_VERTEX_COLOR, "0xFF0000");
|
||||
return Colors.getHexColor(property);
|
||||
}
|
||||
|
||||
// private Color getSelectedEdgeColor() {
|
||||
// return Colors
|
||||
// .getHexColor(displayProperties.getOrDefault(SELECTED_EDGE_COLOR, "0xFF0000"));
|
||||
// }
|
||||
private Color getSelectedEdgeColor() {
|
||||
String property = displayProperties.getOrDefault(SELECTED_EDGE_COLOR, "0xFF0000");
|
||||
return Colors.getHexColor(property);
|
||||
}
|
||||
|
||||
JComponent getComponent() {
|
||||
JComponent component = viewer.getComponent();
|
||||
|
@ -565,9 +587,9 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
|||
private void createAndDisplaySubGraph() {
|
||||
GraphDisplay display = graphDisplayProvider.getGraphDisplay(false, TaskMonitor.DUMMY);
|
||||
try {
|
||||
display.setGraph(createSubGraph(), "SubGraph", false, TaskMonitor.DUMMY);
|
||||
display.setGraph(createSubGraph(), title + " - Sub-graph", false, TaskMonitor.DUMMY);
|
||||
display.setGraphDisplayListener(listener.cloneWith(display));
|
||||
addedActions.forEach(display::addAction);
|
||||
copyActionsToNewGraph((DefaultGraphDisplay) display);
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
// using Dummy, so can't happen
|
||||
|
@ -580,11 +602,11 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
|||
|
||||
AttributedGraph newGraph = new AttributedGraph();
|
||||
subGraph.vertexSet().forEach(newGraph::addVertex);
|
||||
subGraph.edgeSet().forEach(e -> {
|
||||
for (AttributedEdge e : subGraph.edgeSet()) {
|
||||
AttributedVertex source = subGraph.getEdgeSource(e);
|
||||
AttributedVertex target = subGraph.getEdgeTarget(e);
|
||||
newGraph.addEdge(source, target, e);
|
||||
});
|
||||
}
|
||||
return newGraph;
|
||||
}
|
||||
|
||||
|
@ -599,10 +621,10 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
|||
private Set<AttributedVertex> getSourceVerticesFromSelected() {
|
||||
Set<AttributedVertex> sources = new HashSet<>();
|
||||
Set<AttributedVertex> selectedVertices = getSelectedVertices();
|
||||
selectedVertices.forEach(v -> {
|
||||
for (AttributedVertex v : selectedVertices) {
|
||||
Set<AttributedEdge> edges = graph.incomingEdgesOf(v);
|
||||
edges.forEach(e -> sources.add(graph.getEdgeSource(e)));
|
||||
});
|
||||
}
|
||||
return sources;
|
||||
}
|
||||
|
||||
|
@ -617,10 +639,10 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
|||
private Set<AttributedVertex> getTargetVerticesFromSelected() {
|
||||
Set<AttributedVertex> targets = new HashSet<>();
|
||||
Set<AttributedVertex> selectedVertices = getSelectedVertices();
|
||||
selectedVertices.forEach(v -> {
|
||||
for (AttributedVertex v : selectedVertices) {
|
||||
Set<AttributedEdge> edges = graph.outgoingEdgesOf(v);
|
||||
edges.forEach(e -> targets.add(graph.getEdgeTarget(e)));
|
||||
});
|
||||
}
|
||||
return targets;
|
||||
}
|
||||
|
||||
|
@ -665,14 +687,14 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
|||
try {
|
||||
MutableSelectedState<AttributedVertex> selectedVertexState =
|
||||
viewer.getSelectedVertexState();
|
||||
graph.vertexSet().forEach(v -> {
|
||||
for (AttributedVertex v : graph.vertexSet()) {
|
||||
if (selectedVertexState.isSelected(v)) {
|
||||
selectedVertexState.deselect(v);
|
||||
}
|
||||
else {
|
||||
selectedVertexState.select(v);
|
||||
}
|
||||
});
|
||||
}
|
||||
Set<AttributedVertex> selected = selectedVertexState.getSelected();
|
||||
notifySelectionChanged(selected);
|
||||
}
|
||||
|
@ -1231,6 +1253,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
|||
RenderContext<AttributedVertex, AttributedEdge> renderContext = vv.getRenderContext();
|
||||
|
||||
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(
|
||||
|
@ -1239,22 +1262,17 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
|||
|
||||
// selected edges will be drawn with a wider stroke
|
||||
renderContext.setEdgeStrokeFunction(
|
||||
e -> vv.getSelectedEdges().contains(e) ? new BasicStroke(20.f)
|
||||
e -> isSelected(e) ? new BasicStroke(20.f)
|
||||
: ProgramGraphFunctions.getEdgeStroke(e));
|
||||
|
||||
// selected edges will be drawn in red (instead of default)
|
||||
Color selectedEdgeColor =
|
||||
Colors.getHexColor(
|
||||
displayProperties.getOrDefault("selectedEdgeColor", "0xFF0000"));
|
||||
Color selectedEdgeColor = getSelectedEdgeColor();
|
||||
renderContext.setEdgeDrawPaintFunction(
|
||||
e -> vv.getSelectedEdges().contains(e) ? selectedEdgeColor
|
||||
: Colors.getColor(e));
|
||||
e -> isSelected(e) ? selectedEdgeColor : Colors.getColor(e));
|
||||
renderContext.setArrowDrawPaintFunction(
|
||||
e -> vv.getSelectedEdges().contains(e) ? selectedEdgeColor
|
||||
: Colors.getColor(e));
|
||||
e -> isSelected(e) ? selectedEdgeColor : Colors.getColor(e));
|
||||
renderContext.setArrowFillPaintFunction(
|
||||
e -> vv.getSelectedEdges().contains(e) ? selectedEdgeColor
|
||||
: Colors.getColor(e));
|
||||
e -> isSelected(e) ? selectedEdgeColor : Colors.getColor(e));
|
||||
|
||||
// assign the shapes to the modal renderer
|
||||
ModalRenderer<AttributedVertex, AttributedEdge> modalRenderer = vv.getRenderer();
|
||||
|
@ -1262,12 +1280,16 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
|||
|
||||
Renderer.Vertex<AttributedVertex, AttributedEdge> vertexRenderer =
|
||||
modalRenderer.getVertexRenderer(LIGHTWEIGHT);
|
||||
|
||||
// cause the lightweight (optimized) renderer to use the vertex shapes instead
|
||||
// of using default shapes.
|
||||
|
||||
if (vertexRenderer instanceof LightweightVertexRenderer) {
|
||||
Function<AttributedVertex, Shape> vertexShapeFunction =
|
||||
renderContext.getVertexShapeFunction();
|
||||
LightweightVertexRenderer<AttributedVertex, AttributedEdge> lightweightVertexRenderer =
|
||||
(LightweightVertexRenderer<AttributedVertex, AttributedEdge>) vertexRenderer;
|
||||
lightweightVertexRenderer.setVertexShapeFunction(ProgramGraphFunctions::getVertexShape);
|
||||
lightweightVertexRenderer.setVertexShapeFunction(vertexShapeFunction);
|
||||
}
|
||||
|
||||
renderContext.setVertexLabelRenderer(new JLabelVertexLabelRenderer(Color.black));
|
||||
|
@ -1294,27 +1316,25 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
|||
RenderContext<AttributedVertex, AttributedEdge> renderContext = vv.getRenderContext();
|
||||
String useIcons =
|
||||
displayProperties.getOrDefault(DISPLAY_VERTICES_AS_ICONS, Boolean.TRUE.toString());
|
||||
Function<Shape, Rectangle> toRectangle = s -> RectangleUtils.convert(s.getBounds2D());
|
||||
if (Boolean.parseBoolean(useIcons)) {
|
||||
// set up the shape and color functions
|
||||
IconShapeFunction<AttributedVertex> nodeImageShapeFunction =
|
||||
IconShapeFunction<AttributedVertex> nodeShaper =
|
||||
new IconShapeFunction<>(new EllipseShapeFunction<>());
|
||||
|
||||
nodeImageShapeFunction.setIconFunction(iconCache::get);
|
||||
renderContext.setVertexShapeFunction(nodeImageShapeFunction);
|
||||
nodeShaper.setIconFunction(iconCache::get);
|
||||
renderContext.setVertexShapeFunction(nodeShaper);
|
||||
renderContext.setVertexIconFunction(iconCache::get);
|
||||
|
||||
vv.setInitialDimensionFunction(InitialDimensionFunction
|
||||
.builder(
|
||||
nodeImageShapeFunction
|
||||
.andThen(s -> RectangleUtils.convert(s.getBounds2D())))
|
||||
.builder(nodeShaper.andThen(toRectangle))
|
||||
.build());
|
||||
}
|
||||
else {
|
||||
vv.getRenderContext().setVertexShapeFunction(ProgramGraphFunctions::getVertexShape);
|
||||
vv.setInitialDimensionFunction(InitialDimensionFunction
|
||||
.builder(
|
||||
renderContext.getVertexShapeFunction()
|
||||
.andThen(s -> RectangleUtils.convert(s.getBounds2D())))
|
||||
.builder(renderContext.getVertexShapeFunction()
|
||||
.andThen(toRectangle))
|
||||
.build());
|
||||
vv.getRenderContext().setVertexLabelFunction(Object::toString);
|
||||
vv.getRenderContext()
|
||||
|
@ -1324,52 +1344,40 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Item listener for selection changes in the graph with the additional
|
||||
* capability of being able to disable the listener without removing it.
|
||||
*/
|
||||
class SwitchableSelectionItemListener implements ItemListener {
|
||||
boolean enabled = true;
|
||||
private void copyActionsToNewGraph(DefaultGraphDisplay display) {
|
||||
|
||||
@Override
|
||||
public void itemStateChanged(ItemEvent e) {
|
||||
if (enabled) {
|
||||
Swing.runLater(() -> run(e));
|
||||
}
|
||||
for (DockingActionIf action : addedActions) {
|
||||
if (display.containsAction(action)) {
|
||||
// ignore actions added by the graph itself and any actions that the end user may
|
||||
// accidentally add more than once
|
||||
continue;
|
||||
}
|
||||
|
||||
private void run(ItemEvent e) {
|
||||
// there was a change in the set of selected vertices.
|
||||
// if the focused vertex is null, set it from one of the selected
|
||||
// vertices
|
||||
if (e.getStateChange() == ItemEvent.SELECTED) {
|
||||
Set<AttributedVertex> selectedVertices = getSelectedVertices();
|
||||
notifySelectionChanged(new HashSet<AttributedVertex>(selectedVertices));
|
||||
|
||||
if (selectedVertices.size() == 1) {
|
||||
// if only one vertex was selected, make it the focused vertex
|
||||
setFocusedVertex(selectedVertices.stream().findFirst().get());
|
||||
}
|
||||
else if (DefaultGraphDisplay.this.focusedVertex == null) {
|
||||
// if there is currently no focused Vertex, attempt to get
|
||||
// one from the selectedVertices
|
||||
setFocusedVertex(selectedVertices.stream().findFirst().orElse(null));
|
||||
}
|
||||
}
|
||||
else if (e.getStateChange() == ItemEvent.DESELECTED) {
|
||||
Set<AttributedVertex> selectedVertices = getSelectedVertices();
|
||||
notifySelectionChanged(selectedVertices);
|
||||
}
|
||||
viewer.repaint();
|
||||
display.addAction(new DockingActionProxy(action));
|
||||
}
|
||||
|
||||
void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
private boolean containsAction(DockingActionIf action) {
|
||||
|
||||
String name = action.getFullName(); // name and owner
|
||||
for (DockingActionIf existingAction : addedActions) {
|
||||
if (name.equals(existingAction.getFullName())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAction(DockingAction action) {
|
||||
public void addAction(DockingActionIf action) {
|
||||
|
||||
if (containsAction(action)) {
|
||||
Msg.warn(this, "Action with same name and owner already exixts in graph: " +
|
||||
action.getFullName());
|
||||
return;
|
||||
}
|
||||
|
||||
addedActions.add(action);
|
||||
Swing.runLater(() -> componentProvider.addLocalAction(action));
|
||||
}
|
||||
|
@ -1456,6 +1464,10 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
|||
});
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Inner Classes
|
||||
//==================================================================================================
|
||||
|
||||
// class passed to the PopupRegulator to help construct info popups for the graph
|
||||
private class GraphDisplayPopupSource implements PopupSource<AttributedVertex, AttributedEdge> {
|
||||
|
||||
|
@ -1554,4 +1566,49 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
|||
// this graph display does not have a notion of emphasizing
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Item listener for selection changes in the graph with the additional
|
||||
* capability of being able to disable the listener without removing it.
|
||||
*/
|
||||
private class SwitchableSelectionItemListener implements ItemListener {
|
||||
boolean enabled = true;
|
||||
|
||||
@Override
|
||||
public void itemStateChanged(ItemEvent e) {
|
||||
if (enabled) {
|
||||
Swing.runLater(() -> run(e));
|
||||
}
|
||||
}
|
||||
|
||||
private void run(ItemEvent e) {
|
||||
// there was a change in the set of selected vertices.
|
||||
// if the focused vertex is null, set it from one of the selected
|
||||
// vertices
|
||||
if (e.getStateChange() == ItemEvent.SELECTED) {
|
||||
Set<AttributedVertex> selectedVertices = getSelectedVertices();
|
||||
notifySelectionChanged(new HashSet<AttributedVertex>(selectedVertices));
|
||||
|
||||
if (selectedVertices.size() == 1) {
|
||||
// if only one vertex was selected, make it the focused vertex
|
||||
setFocusedVertex(selectedVertices.stream().findFirst().get());
|
||||
}
|
||||
else if (DefaultGraphDisplay.this.focusedVertex == null) {
|
||||
// if there is currently no focused Vertex, attempt to get
|
||||
// one from the selectedVertices
|
||||
setFocusedVertex(selectedVertices.stream().findFirst().orElse(null));
|
||||
}
|
||||
}
|
||||
else if (e.getStateChange() == ItemEvent.DESELECTED) {
|
||||
Set<AttributedVertex> selectedVertices = getSelectedVertices();
|
||||
notifySelectionChanged(selectedVertices);
|
||||
}
|
||||
viewer.repaint();
|
||||
}
|
||||
|
||||
void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -57,10 +57,11 @@ public class DefaultGraphDisplayComponentProvider extends ComponentProviderAdapt
|
|||
public void closeComponent() {
|
||||
if (display != null) {
|
||||
super.closeComponent();
|
||||
// to prevent looping, null out display before callings its close method.
|
||||
// to prevent looping, null out display before calling its close method.
|
||||
GraphDisplay closingDisplay = display;
|
||||
display = null;
|
||||
closingDisplay.close();
|
||||
removeAllLocalActions();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -362,9 +362,9 @@
|
|||
|
||||
<BLOCKQUOTE>
|
||||
|
||||
<H3><A NAME="Rename_Vertex"></A>Rename Vertex</H3>
|
||||
<H3><A NAME="Rename_Symbol"></A>Rename Symbol</H3>
|
||||
<BLOCKQUOTE>
|
||||
<P>Allows the user to rename the symbol represented by the given vertex.
|
||||
<P>Allows the user to rename the symbol in the program represented by the given vertex.
|
||||
</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
|
|
|
@ -18,8 +18,10 @@ package ghidra.graph.program;
|
|||
import java.awt.Color;
|
||||
import java.util.*;
|
||||
|
||||
import docking.action.builder.ActionBuilder;
|
||||
import docking.widgets.EventTrigger;
|
||||
import ghidra.app.plugin.core.colorizer.ColorizingService;
|
||||
import ghidra.app.util.AddEditDialog;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.block.*;
|
||||
|
@ -28,8 +30,7 @@ import ghidra.program.model.symbol.*;
|
|||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.program.util.ProgramSelection;
|
||||
import ghidra.service.graph.*;
|
||||
import ghidra.util.HTMLUtilities;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.exception.GraphException;
|
||||
import ghidra.util.task.Task;
|
||||
|
@ -145,6 +146,7 @@ public class BlockGraphTask extends Task {
|
|||
GraphDisplay display = graphProvider.getGraphDisplay(reuseGraph, monitor);
|
||||
BlockModelGraphDisplayListener listener =
|
||||
new BlockModelGraphDisplayListener(tool, blockModel, display);
|
||||
addActions(display, v -> listener.getAddress(v));
|
||||
display.setGraphDisplayListener(listener);
|
||||
|
||||
if (showCode) {
|
||||
|
@ -175,6 +177,37 @@ public class BlockGraphTask extends Task {
|
|||
}
|
||||
}
|
||||
|
||||
private void addActions(GraphDisplay display,
|
||||
java.util.function.Function<AttributedVertex, Address> addressFunction) {
|
||||
|
||||
display.addAction(new ActionBuilder("Rename Symbol", "Block Graph")
|
||||
.popupMenuPath("Rename Symbol")
|
||||
.withContext(VertexGraphActionContext.class)
|
||||
.helpLocation(new HelpLocation("ProgramGraphPlugin", "Rename_Symbol"))
|
||||
// only enable action when vertex corresponds to an address
|
||||
.enabledWhen(c -> addressFunction.apply(c.getClickedVertex()) != null)
|
||||
.onAction(c -> updateVertexName(addressFunction, c))
|
||||
.build());
|
||||
}
|
||||
|
||||
private void updateVertexName(
|
||||
java.util.function.Function<AttributedVertex, Address> addressFunction,
|
||||
VertexGraphActionContext context) {
|
||||
|
||||
AttributedVertex vertex = context.getClickedVertex();
|
||||
Address address = addressFunction.apply(vertex);
|
||||
Symbol symbol = program.getSymbolTable().getPrimarySymbol(address);
|
||||
|
||||
if (symbol == null) {
|
||||
AddEditDialog dialog = new AddEditDialog("Create Label", tool);
|
||||
dialog.addLabel(address, program, context.getComponentProvider());
|
||||
}
|
||||
else {
|
||||
AddEditDialog dialog = new AddEditDialog("Edit Label", tool);
|
||||
dialog.editLabel(symbol, program, context.getComponentProvider());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the maximum number of code lines which will be used per block when
|
||||
* showCode is enabled.
|
||||
|
@ -318,10 +351,8 @@ public class BlockGraphTask extends Task {
|
|||
CodeBlockReference cbRef = refIter.next();
|
||||
|
||||
CodeBlock db = cbRef.getDestinationBlock();
|
||||
|
||||
// must be a reference to a data block
|
||||
if (db == null) {
|
||||
continue;
|
||||
continue; // must be a reference to a data block
|
||||
}
|
||||
|
||||
// don't include destination if it does not overlap selection
|
||||
|
@ -336,7 +367,6 @@ public class BlockGraphTask extends Task {
|
|||
}
|
||||
|
||||
// put the edge in the graph
|
||||
String edgeAddr = cbRef.getReferent().toString();
|
||||
AttributedEdge newEdge = graph.addEdge(fromVertex, toVertex);
|
||||
|
||||
// set it's attributes (really its name)
|
||||
|
|
|
@ -17,16 +17,13 @@ package ghidra.graph.program;
|
|||
|
||||
import java.util.*;
|
||||
|
||||
import docking.action.builder.ActionBuilder;
|
||||
import ghidra.app.plugin.core.graph.AddressBasedGraphDisplayListener;
|
||||
import ghidra.app.util.AddEditDialog;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.block.*;
|
||||
import ghidra.program.model.symbol.Symbol;
|
||||
import ghidra.program.model.symbol.SymbolTable;
|
||||
import ghidra.service.graph.*;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
|
@ -41,18 +38,11 @@ public class BlockModelGraphDisplayListener extends AddressBasedGraphDisplayList
|
|||
GraphDisplay display) {
|
||||
super(tool, blockModel.getProgram(), display);
|
||||
this.blockModel = blockModel;
|
||||
addActions(display);
|
||||
}
|
||||
|
||||
private void addActions(GraphDisplay display) {
|
||||
display.addAction(new ActionBuilder("Rename Vertex", "Block Graph")
|
||||
.popupMenuPath("Rename Vertex")
|
||||
.withContext(VertexGraphActionContext.class)
|
||||
.helpLocation(new HelpLocation("ProgramGraphPlugin", "Rename Vertex"))
|
||||
// only enable action when vertex corresponds to an address
|
||||
.enabledWhen(c -> getAddress(c.getClickedVertex().getId()) != null)
|
||||
.onAction(this::updateVertexName)
|
||||
.build());
|
||||
@Override
|
||||
public Address getAddress(AttributedVertex vertex) {
|
||||
return super.getAddress(vertex);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -79,11 +69,24 @@ public class BlockModelGraphDisplayListener extends AddressBasedGraphDisplayList
|
|||
// Identify all blocks which have an entry point within the selection address set
|
||||
Set<AttributedVertex> vertices = new HashSet<>();
|
||||
try {
|
||||
addVerticesForAddresses(addrSet, vertices);
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
// Will not happen with dummyMonitor
|
||||
// Model has already done the work when the graph was created
|
||||
}
|
||||
|
||||
return vertices;
|
||||
}
|
||||
|
||||
private void addVerticesForAddresses(AddressSetView addrSet, Set<AttributedVertex> vertices)
|
||||
throws CancelledException {
|
||||
|
||||
SymbolTable symTable = program.getSymbolTable();
|
||||
CodeBlockIterator cbIter =
|
||||
CodeBlockIterator it =
|
||||
blockModel.getCodeBlocksContaining(addrSet, TaskMonitor.DUMMY);
|
||||
while (cbIter.hasNext()) {
|
||||
CodeBlock block = cbIter.next();
|
||||
while (it.hasNext()) {
|
||||
CodeBlock block = it.next();
|
||||
String addrString;
|
||||
Address addr = block.getFirstStartAddress();
|
||||
if (addr.isExternalAddress()) {
|
||||
|
@ -99,26 +102,33 @@ public class BlockModelGraphDisplayListener extends AddressBasedGraphDisplayList
|
|||
}
|
||||
}
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
// Will not happen with dummyMonitor
|
||||
// Model has already done the work when the graph was created
|
||||
}
|
||||
|
||||
return vertices;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AddressSet getAddresses(Set<AttributedVertex> vertices) {
|
||||
AddressSet addrSet = new AddressSet();
|
||||
|
||||
AddressSet addrSet = new AddressSet();
|
||||
try {
|
||||
// for each address string, translate it into a block
|
||||
// and add it to the address set.
|
||||
for (AttributedVertex vertex : vertices) {
|
||||
addBlockAddresses(addrSet, vertex);
|
||||
}
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
// Will not happen with dummyMonitor
|
||||
// Model has already done the work when the graph was created
|
||||
}
|
||||
return addrSet;
|
||||
}
|
||||
|
||||
private void addBlockAddresses(AddressSet addrSet, AttributedVertex vertex)
|
||||
throws CancelledException {
|
||||
|
||||
Address blockAddr = getAddress(vertex);
|
||||
if (!isValidAddress(blockAddr)) {
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
|
||||
CodeBlock blocks[] = null;
|
||||
if (blockModel != null) {
|
||||
CodeBlock block = blockModel.getCodeBlockAt(blockAddr, TaskMonitor.DUMMY);
|
||||
|
@ -139,13 +149,6 @@ public class BlockModelGraphDisplayListener extends AddressBasedGraphDisplayList
|
|||
addrSet.addRange(blockAddr, blockAddr);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
// Will not happen with dummyMonitor
|
||||
// Model has already done the work when the graph was created
|
||||
}
|
||||
return addrSet;
|
||||
}
|
||||
|
||||
protected boolean isValidAddress(Address addr) {
|
||||
if (addr == null || program == null) {
|
||||
|
@ -154,21 +157,6 @@ public class BlockModelGraphDisplayListener extends AddressBasedGraphDisplayList
|
|||
return program.getMemory().contains(addr) || addr.isExternalAddress();
|
||||
}
|
||||
|
||||
private void updateVertexName(VertexGraphActionContext context) {
|
||||
AttributedVertex vertex = context.getClickedVertex();
|
||||
Address address = getAddress(vertex);
|
||||
Symbol symbol = program.getSymbolTable().getPrimarySymbol(address);
|
||||
|
||||
if (symbol == null) {
|
||||
AddEditDialog dialog = new AddEditDialog("Create Label", tool);
|
||||
dialog.addLabel(address, program, context.getComponentProvider());
|
||||
}
|
||||
else {
|
||||
AddEditDialog dialog = new AddEditDialog("Edit Label", tool);
|
||||
dialog.editLabel(symbol, program, context.getComponentProvider());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public GraphDisplayListener cloneWith(GraphDisplay newGraphDisplay) {
|
||||
return new BlockModelGraphDisplayListener(tool, blockModel, newGraphDisplay);
|
||||
|
|
|
@ -18,7 +18,7 @@ package ghidra.graph.program;
|
|||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import docking.action.DockingAction;
|
||||
import docking.action.DockingActionIf;
|
||||
import docking.widgets.EventTrigger;
|
||||
import ghidra.service.graph.*;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
|
@ -74,7 +74,8 @@ public class TestGraphDisplay implements GraphDisplay {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void setVertexLabelAttribute(String attributeName, int alignment, int size, boolean monospace,
|
||||
public void setVertexLabelAttribute(String attributeName, int alignment, int size,
|
||||
boolean monospace,
|
||||
int maxLines) {
|
||||
// nothing
|
||||
}
|
||||
|
@ -116,8 +117,7 @@ public class TestGraphDisplay implements GraphDisplay {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void addAction(DockingAction action) {
|
||||
public void addAction(DockingActionIf action) {
|
||||
// do nothing, actions are not supported by this display
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ package ghidra.service.graph;
|
|||
|
||||
import java.util.Set;
|
||||
|
||||
import docking.action.DockingAction;
|
||||
import docking.action.DockingActionIf;
|
||||
import docking.widgets.EventTrigger;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
@ -43,7 +43,7 @@ public interface GraphDisplay {
|
|||
public void setGraphDisplayListener(GraphDisplayListener listener);
|
||||
|
||||
/**
|
||||
* Tells the graph display window to focus the vertex with the given id.
|
||||
* Tells the graph display window to focus the vertex with the given id
|
||||
*
|
||||
* @param vertex the vertex to focus
|
||||
* @param eventTrigger Provides a hint to the GraphDisplay as to why we are updating the
|
||||
|
@ -63,7 +63,8 @@ public interface GraphDisplay {
|
|||
|
||||
/**
|
||||
* Returns the currently focused vertex or null if no vertex is focused
|
||||
* @return the currently focused vertex or null if no vertex is focused.
|
||||
*
|
||||
* @return the currently focused vertex or null if no vertex is focused
|
||||
*/
|
||||
public AttributedVertex getFocusedVertex();
|
||||
|
||||
|
@ -82,6 +83,7 @@ public interface GraphDisplay {
|
|||
|
||||
/**
|
||||
* Returns a set of vertex ids for all the currently selected vertices
|
||||
*
|
||||
* @return a set of vertex ids for all the currently selected vertices
|
||||
*/
|
||||
public Set<AttributedVertex> getSelectedVertices();
|
||||
|
@ -94,7 +96,7 @@ public interface GraphDisplay {
|
|||
/**
|
||||
* Defines a vertex attribute type for this graph window
|
||||
*
|
||||
* @param name the name of the attribute which may be attached to vertices.
|
||||
* @param name the name of the attribute which may be attached to vertices
|
||||
*/
|
||||
public void defineVertexAttribute(String name);
|
||||
|
||||
|
@ -106,22 +108,25 @@ public interface GraphDisplay {
|
|||
public void defineEdgeAttribute(String name);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* Sets the name of the attribute which should be used as the primary vertex label
|
||||
*
|
||||
* @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 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
|
||||
*/
|
||||
public void setVertexLabelAttribute(String attributeName, int alignment, int size, boolean monospace,
|
||||
public void setVertexLabelAttribute(String attributeName, int alignment, int size,
|
||||
boolean monospace,
|
||||
int maxLines);
|
||||
|
||||
/**
|
||||
* Sets the graph to be displayed or consumed by this graph display
|
||||
*
|
||||
* @param graph the graph to display or consume
|
||||
* @param title a title for the graph
|
||||
* @param monitor a {@link TaskMonitor} which can be used to cancel the graphing operation
|
||||
* @param append if true, append the new graph to any existing graph.
|
||||
* @param append if true, append the new graph to any existing graph
|
||||
* @throws CancelledException thrown if the graphing operation was cancelled
|
||||
*/
|
||||
public void setGraph(AttributedGraph graph, String title, boolean append,
|
||||
|
@ -135,6 +140,7 @@ public interface GraphDisplay {
|
|||
|
||||
/**
|
||||
* Updates a vertex to a new name
|
||||
*
|
||||
* @param vertex the vertex to rename
|
||||
* @param newName the new name for the vertex
|
||||
*/
|
||||
|
@ -142,6 +148,7 @@ public interface GraphDisplay {
|
|||
|
||||
/**
|
||||
* Returns the title of the current graph
|
||||
*
|
||||
* @return the title of the current graph
|
||||
*/
|
||||
public String getGraphTitle();
|
||||
|
@ -149,7 +156,8 @@ public interface GraphDisplay {
|
|||
/**
|
||||
* Adds the action to the graph display. Not all GraphDisplays support adding custom
|
||||
* actions, so this may have no effect.
|
||||
* @param action the action to add.
|
||||
*
|
||||
* @param action the action to add
|
||||
*/
|
||||
public void addAction(DockingAction action);
|
||||
public void addAction(DockingActionIf action);
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package ghidra.service.graph;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import docking.ComponentProvider;
|
||||
|
@ -31,7 +32,7 @@ public class VertexGraphActionContext extends GraphActionContext {
|
|||
AttributedVertex locatedVertex, AttributedVertex clickedVertex) {
|
||||
|
||||
super(componentProvider, graph, selectedVertices, locatedVertex);
|
||||
this.clickedVertex = clickedVertex;
|
||||
this.clickedVertex = Objects.requireNonNull(clickedVertex);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue