diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/services/GraphDisplayBroker.java b/Ghidra/Features/Base/src/main/java/ghidra/app/services/GraphDisplayBroker.java index 69eb011903..a3156f3047 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/services/GraphDisplayBroker.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/services/GraphDisplayBroker.java @@ -68,9 +68,8 @@ public interface GraphDisplayBroker { /** * A convenience method for getting a {@link GraphDisplay} from the currently active provider * - *

This method allows users to override default graph properties defined by - * jungrapht. See that library for a complete list of available properties. - * Default properties can be changed in the {@code jungrapht.properties} file. + *

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 diff --git a/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/export/ExportAttributedGraphDisplay.java b/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/export/ExportAttributedGraphDisplay.java index 2aa5a928e0..85b415c096 100644 --- a/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/export/ExportAttributedGraphDisplay.java +++ b/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/export/ExportAttributedGraphDisplay.java @@ -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 } diff --git a/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/DefaultGraphDisplay.java b/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/DefaultGraphDisplay.java index e2bc4e9098..a3074cf957 100644 --- a/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/DefaultGraphDisplay.java +++ b/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/DefaultGraphDisplay.java @@ -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 + * + *

This graph uses the following properties: + *

+ * */ 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 displayProperties = new HashMap<>(); + private Set addedActions = new LinkedHashSet<>(); private GraphDisplayListener listener = new DummyGraphDisplayListener(); private String title; @@ -175,8 +199,6 @@ public class DefaultGraphDisplay implements GraphDisplay { private PopupRegulator popupRegulator; private GhidraGraphCollapser graphCollapser; - private Set 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 getSourceVerticesFromSelected() { Set sources = new HashSet<>(); Set selectedVertices = getSelectedVertices(); - selectedVertices.forEach(v -> { + for (AttributedVertex v : selectedVertices) { Set 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 getTargetVerticesFromSelected() { Set targets = new HashSet<>(); Set selectedVertices = getSelectedVertices(); - selectedVertices.forEach(v -> { + for (AttributedVertex v : selectedVertices) { Set 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 selectedVertexState = viewer.getSelectedVertexState(); - graph.vertexSet().forEach(v -> { + for (AttributedVertex v : graph.vertexSet()) { if (selectedVertexState.isSelected(v)) { selectedVertexState.deselect(v); } else { selectedVertexState.select(v); } - }); + } Set selected = selectedVertexState.getSelected(); notifySelectionChanged(selected); } @@ -1231,6 +1253,7 @@ public class DefaultGraphDisplay implements GraphDisplay { RenderContext 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 modalRenderer = vv.getRenderer(); @@ -1262,12 +1280,16 @@ public class DefaultGraphDisplay implements GraphDisplay { Renderer.Vertex vertexRenderer = modalRenderer.getVertexRenderer(LIGHTWEIGHT); + // cause the lightweight (optimized) renderer to use the vertex shapes instead // of using default shapes. + if (vertexRenderer instanceof LightweightVertexRenderer) { + Function vertexShapeFunction = + renderContext.getVertexShapeFunction(); LightweightVertexRenderer lightweightVertexRenderer = (LightweightVertexRenderer) vertexRenderer; - lightweightVertexRenderer.setVertexShapeFunction(ProgramGraphFunctions::getVertexShape); + lightweightVertexRenderer.setVertexShapeFunction(vertexShapeFunction); } renderContext.setVertexLabelRenderer(new JLabelVertexLabelRenderer(Color.black)); @@ -1294,27 +1316,25 @@ public class DefaultGraphDisplay implements GraphDisplay { RenderContext renderContext = vv.getRenderContext(); String useIcons = displayProperties.getOrDefault(DISPLAY_VERTICES_AS_ICONS, Boolean.TRUE.toString()); + Function toRectangle = s -> RectangleUtils.convert(s.getBounds2D()); if (Boolean.parseBoolean(useIcons)) { // set up the shape and color functions - IconShapeFunction nodeImageShapeFunction = + IconShapeFunction 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; } + + display.addAction(new DockingActionProxy(action)); } - 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 selectedVertices = getSelectedVertices(); - notifySelectionChanged(new HashSet(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 selectedVertices = getSelectedVertices(); - notifySelectionChanged(selectedVertices); - } - viewer.repaint(); - } + private boolean containsAction(DockingActionIf action) { - void setEnabled(boolean enabled) { - this.enabled = enabled; + 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 { @@ -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 selectedVertices = getSelectedVertices(); + notifySelectionChanged(new HashSet(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 selectedVertices = getSelectedVertices(); + notifySelectionChanged(selectedVertices); + } + viewer.repaint(); + } + + void setEnabled(boolean enabled) { + this.enabled = enabled; + } + } + } diff --git a/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/DefaultGraphDisplayComponentProvider.java b/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/DefaultGraphDisplayComponentProvider.java index 7658bb42db..7531194e05 100644 --- a/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/DefaultGraphDisplayComponentProvider.java +++ b/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/DefaultGraphDisplayComponentProvider.java @@ -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(); } } diff --git a/Ghidra/Features/ProgramGraph/src/main/help/help/topics/ProgramGraphPlugin/ProgramGraph.htm b/Ghidra/Features/ProgramGraph/src/main/help/help/topics/ProgramGraphPlugin/ProgramGraph.htm index a67e138644..1a71d1c47e 100644 --- a/Ghidra/Features/ProgramGraph/src/main/help/help/topics/ProgramGraphPlugin/ProgramGraph.htm +++ b/Ghidra/Features/ProgramGraph/src/main/help/help/topics/ProgramGraphPlugin/ProgramGraph.htm @@ -362,9 +362,9 @@
-

Rename Vertex

+

Rename Symbol

-

Allows the user to rename the symbol represented by the given vertex. +

Allows the user to rename the symbol in the program represented by the given vertex.

diff --git a/Ghidra/Features/ProgramGraph/src/main/java/ghidra/graph/program/BlockGraphTask.java b/Ghidra/Features/ProgramGraph/src/main/java/ghidra/graph/program/BlockGraphTask.java index ed2cf88e91..c524e880c2 100644 --- a/Ghidra/Features/ProgramGraph/src/main/java/ghidra/graph/program/BlockGraphTask.java +++ b/Ghidra/Features/ProgramGraph/src/main/java/ghidra/graph/program/BlockGraphTask.java @@ -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 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 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) diff --git a/Ghidra/Features/ProgramGraph/src/main/java/ghidra/graph/program/BlockModelGraphDisplayListener.java b/Ghidra/Features/ProgramGraph/src/main/java/ghidra/graph/program/BlockModelGraphDisplayListener.java index 7a03962fca..2acd7567ca 100644 --- a/Ghidra/Features/ProgramGraph/src/main/java/ghidra/graph/program/BlockModelGraphDisplayListener.java +++ b/Ghidra/Features/ProgramGraph/src/main/java/ghidra/graph/program/BlockModelGraphDisplayListener.java @@ -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,25 +69,7 @@ public class BlockModelGraphDisplayListener extends AddressBasedGraphDisplayList // Identify all blocks which have an entry point within the selection address set Set vertices = new HashSet<>(); try { - SymbolTable symTable = program.getSymbolTable(); - CodeBlockIterator cbIter = - blockModel.getCodeBlocksContaining(addrSet, TaskMonitor.DUMMY); - while (cbIter.hasNext()) { - CodeBlock block = cbIter.next(); - String addrString; - Address addr = block.getFirstStartAddress(); - if (addr.isExternalAddress()) { - Symbol s = symTable.getPrimarySymbol(addr); - addrString = s.getName(true); - } - else { - addrString = addr.toString(); - } - AttributedVertex vertex = graphDisplay.getGraph().getVertex(addrString); - if (vertex != null) { - vertices.add(vertex); - } - } + addVerticesForAddresses(addrSet, vertices); } catch (CancelledException e) { // Will not happen with dummyMonitor @@ -107,37 +79,39 @@ public class BlockModelGraphDisplayListener extends AddressBasedGraphDisplayList return vertices; } + private void addVerticesForAddresses(AddressSetView addrSet, Set vertices) + throws CancelledException { + + SymbolTable symTable = program.getSymbolTable(); + CodeBlockIterator it = + blockModel.getCodeBlocksContaining(addrSet, TaskMonitor.DUMMY); + while (it.hasNext()) { + CodeBlock block = it.next(); + String addrString; + Address addr = block.getFirstStartAddress(); + if (addr.isExternalAddress()) { + Symbol s = symTable.getPrimarySymbol(addr); + addrString = s.getName(true); + } + else { + addrString = addr.toString(); + } + AttributedVertex vertex = graphDisplay.getGraph().getVertex(addrString); + if (vertex != null) { + vertices.add(vertex); + } + } + } + @Override protected AddressSet getAddresses(Set 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) { - Address blockAddr = getAddress(vertex); - if (!isValidAddress(blockAddr)) { - continue; - } - CodeBlock blocks[] = null; - if (blockModel != null) { - CodeBlock block = blockModel.getCodeBlockAt(blockAddr, TaskMonitor.DUMMY); - if (block != null) { - blocks = new CodeBlock[1]; - blocks[0] = block; - } - else { - blocks = blockModel.getCodeBlocksContaining(blockAddr, TaskMonitor.DUMMY); - } - } - if (blocks != null && blocks.length > 0) { - for (CodeBlock block : blocks) { - addrSet.add(block); - } - } - else { - addrSet.addRange(blockAddr, blockAddr); - } + addBlockAddresses(addrSet, vertex); } } catch (CancelledException e) { @@ -147,6 +121,35 @@ public class BlockModelGraphDisplayListener extends AddressBasedGraphDisplayList return addrSet; } + private void addBlockAddresses(AddressSet addrSet, AttributedVertex vertex) + throws CancelledException { + + Address blockAddr = getAddress(vertex); + if (!isValidAddress(blockAddr)) { + return; + } + + CodeBlock blocks[] = null; + if (blockModel != null) { + CodeBlock block = blockModel.getCodeBlockAt(blockAddr, TaskMonitor.DUMMY); + if (block != null) { + blocks = new CodeBlock[1]; + blocks[0] = block; + } + else { + blocks = blockModel.getCodeBlocksContaining(blockAddr, TaskMonitor.DUMMY); + } + } + if (blocks != null && blocks.length > 0) { + for (CodeBlock block : blocks) { + addrSet.add(block); + } + } + else { + addrSet.addRange(blockAddr, blockAddr); + } + } + protected boolean isValidAddress(Address addr) { if (addr == null || program == null) { return false; @@ -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); diff --git a/Ghidra/Features/ProgramGraph/src/test/java/ghidra/graph/program/TestGraphDisplay.java b/Ghidra/Features/ProgramGraph/src/test/java/ghidra/graph/program/TestGraphDisplay.java index 77b6364441..61c2d279c0 100644 --- a/Ghidra/Features/ProgramGraph/src/test/java/ghidra/graph/program/TestGraphDisplay.java +++ b/Ghidra/Features/ProgramGraph/src/test/java/ghidra/graph/program/TestGraphDisplay.java @@ -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 } - } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/service/graph/GraphDisplay.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/service/graph/GraphDisplay.java index 6a64ebb425..696671614e 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/service/graph/GraphDisplay.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/service/graph/GraphDisplay.java @@ -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 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); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/service/graph/VertexGraphActionContext.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/service/graph/VertexGraphActionContext.java index f155ca3023..192aa9175c 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/service/graph/VertexGraphActionContext.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/service/graph/VertexGraphActionContext.java @@ -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); } /**