From 1fd6b54f07bbf5d1f7aab50e1898e684e774fce2 Mon Sep 17 00:00:00 2001 From: ghidravore Date: Wed, 18 Aug 2021 12:28:12 -0400 Subject: [PATCH] fixed graph test issue (and a few other improviements)> Mainly protecting against creating ridiculously large icons in tests because of a strange headless environment. --- .../visualization/DefaultGraphDisplay.java | 24 +++---- .../visualization/DefaultGraphRenderer.java | 72 +++++++++++++------ .../service/graph/GraphDisplayOptions.java | 63 ++++++++++------ .../graph/GraphDisplayOptionsBuilder.java | 42 ++++++----- .../graph/GraphDisplayOptionsTest.java | 9 ++- .../java/ghidra/graph/GraphActionsTest.java | 14 +++- 6 files changed, 149 insertions(+), 75 deletions(-) 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 0ba0318909..8868e1a795 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 @@ -94,7 +94,6 @@ public class DefaultGraphDisplay implements GraphDisplay { private static final String ACTION_OWNER = "GraphServices"; - private static final int MAX_NODES = Integer.getInteger("maxNodes", 10000); private static final Dimension PREFERRED_VIEW_SIZE = new Dimension(1000, 1000); private static final Dimension PREFERRED_LAYOUT_SIZE = new Dimension(3000, 3000); @@ -273,16 +272,16 @@ public class DefaultGraphDisplay implements GraphDisplay { Lens lens = Lens.builder().lensShape(Lens.Shape.RECTANGLE).magnification(3.f).build(); lens.setMagnification(2.f); LensMagnificationGraphMousePlugin magnificationPlugin = - new LensMagnificationGraphMousePlugin(1.f, 60.f, .2f) { - // Override to address a bug when using a high resolution mouse wheel. - // May be removed when jungrapht-visualization version is updated - @Override - public void mouseWheelMoved(MouseWheelEvent e) { - if (e.getWheelRotation() != 0) { - super.mouseWheelMoved(e); - } + new LensMagnificationGraphMousePlugin(1.f, 60.f, .2f) { + // Override to address a bug when using a high resolution mouse wheel. + // May be removed when jungrapht-visualization version is updated + @Override + public void mouseWheelMoved(MouseWheelEvent e) { + if (e.getWheelRotation() != 0) { + super.mouseWheelMoved(e); } - }; + } + }; MutableTransformer transformer = viewer.getRenderContext() .getMultiLayerTransformer() @@ -1158,9 +1157,10 @@ public class DefaultGraphDisplay implements GraphDisplay { this.title = title; componentProvider.setTitle(title); int count = graph.getVertexCount(); - if (count > MAX_NODES) { + if (count > options.getMaxNodeCount()) { Msg.showWarn(this, null, "Graph Not Rendered - Too many nodes!", - "Exceeded limit of " + MAX_NODES + " nodes.\n\n Graph contained " + count + + "Exceeded limit of " + options.getMaxNodeCount() + " nodes.\n\n Graph contained " + + count + " nodes!"); graph = new AttributedGraph("Aborted", graph.getGraphType(), "Too Many Nodes"); graph.addVertex("1", "Graph Aborted"); diff --git a/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/DefaultGraphRenderer.java b/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/DefaultGraphRenderer.java index 33337ca102..3ecb684cb5 100644 --- a/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/DefaultGraphRenderer.java +++ b/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/DefaultGraphRenderer.java @@ -47,8 +47,18 @@ public class DefaultGraphRenderer implements GraphRenderer { private static final double ARROW_WIDTH_TO_LENGTH_RATIO = 1.3; private static final int DEFAULT_MARGIN_BORDER_SIZE = 4; private static final int DEFAULT_STROKE_THICKNESS = 6; - // scale factor so the icons can be rendered smaller so that fonts read better when zoomed out a bit - private static final int ICON_ZOOM = 5; + + // This is an arbitrary scale factor applied after creating a node's icon + // This somehow causes the low level jungrapht layout to perform better + // If the icon is not zoomed, the icons are rendered too small and too far apart + // somehow, by giving it bigger icons, the layouts seem to produce better results + // When/if this is fixed in the jungrapht library, this can be removed + private static final int ICON_ZOOM = 2; + + // put a limit on node size. Nodes are sized based on user supplied vertex names, so need to + // protect them from becoming too large, so just picked some limits. + private static final int MAX_WIDTH = 500; + private static final int MAX_HEIGHT = 500; private int labelBorderSize = DEFAULT_MARGIN_BORDER_SIZE; private int strokeThickness = DEFAULT_STROKE_THICKNESS; @@ -220,7 +230,6 @@ public class DefaultGraphRenderer implements GraphRenderer { VertexShape vertexShape = options.getVertexShape(vertex); Color vertexColor = options.getVertexColor(vertex); String labelText = options.getVertexLabel(vertex); - return createImage(vertexShape, labelText, vertexColor); } @@ -235,37 +244,47 @@ public class DefaultGraphRenderer implements GraphRenderer { Shape unitShape = vertexShape.getShape(); Rectangle bounds = unitShape.getBounds(); + // this variable attempts to keep the shape's height from being too out of proportion + // from the width. int maxWidthToHeightRatio = vertexShape.getMaxWidthToHeightRatio(); double sizeFactor = vertexShape.getShapeToLabelRatio(); int labelWidth = label.getWidth(); int labelHeight = label.getHeight(); - int iconWidth = - (int) (Math.max(labelWidth, labelHeight * 2.0) * sizeFactor) + strokeThickness; - int iconHeight = - (int) (Math.max(label.getHeight(), labelWidth / maxWidthToHeightRatio) * sizeFactor) + - strokeThickness; + // make height somewhat bigger if label width is really long to avoid really long thin + // nodes + labelHeight = Math.max(labelHeight, labelWidth / maxWidthToHeightRatio); - double scalex = iconWidth / bounds.getWidth(); - double scaley = iconHeight / bounds.getHeight(); + // adjust for shape size factor (some shapes want to be somewhat bigger than the label) + // for example, triangles need to be much bigger to get the text to fit inside the shape, + // whereas, rectangles fit naturally. + int shapeWidth = (int) (labelWidth * sizeFactor); + int shapeHeight = (int) (labelHeight * sizeFactor); + + // compute the amount to scale the shape to fit around the label + double scalex = shapeWidth / bounds.getWidth(); + double scaley = shapeHeight / bounds.getHeight(); Shape scaledShape = AffineTransform.getScaleInstance(scalex, scaley).createTransformedShape(unitShape); + // this determines the vertical positioning of text in the shape + // a value of 0 will put the text at the top, 1 at the bottom, and .5 in the center double labelOffsetRatio = vertexShape.getLabelPosition(); - bounds = scaledShape.getBounds(); + int iconWidth = bounds.width + (2 * strokeThickness); + int iconHeight = bounds.height + (2 * strokeThickness); - int width = bounds.width + 2 * strokeThickness; - int height = bounds.height + strokeThickness; - BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + BufferedImage bufferedImage = + new BufferedImage(iconWidth, iconHeight, BufferedImage.TYPE_INT_ARGB); Graphics2D graphics = bufferedImage.createGraphics(); graphics.setRenderingHints(renderingHints); AffineTransform graphicsTransform = graphics.getTransform(); - graphics.translate(-bounds.x + strokeThickness, -bounds.y + strokeThickness / 2); + // shapes are centered at the origin, so translate the graphics to compensate + graphics.translate(-bounds.x + strokeThickness, -bounds.y + strokeThickness); graphics.setPaint(Color.WHITE); graphics.fill(scaledShape); graphics.setPaint(vertexColor); @@ -273,8 +292,12 @@ public class DefaultGraphRenderer implements GraphRenderer { graphics.draw(scaledShape); graphics.setTransform(graphicsTransform); - int xOffset = (width - label.getWidth()) / 2; - int yOffset = (int) ((height - label.getHeight()) * labelOffsetRatio); + + // center the text horizontally + // position the text vertically based on the shape. + int xOffset = (iconWidth - label.getWidth()) / 2; + int yOffset = (int) ((iconHeight - label.getHeight()) * labelOffsetRatio); + graphics.translate(xOffset, yOffset); graphics.setPaint(Color.black); label.paint(graphics); @@ -282,19 +305,26 @@ public class DefaultGraphRenderer implements GraphRenderer { graphics.setTransform(graphicsTransform); // restore the original transform graphics.dispose(); Image scaledImage = - ImageUtils.createScaledImage(bufferedImage, width * ICON_ZOOM, height * ICON_ZOOM, + ImageUtils.createScaledImage(bufferedImage, iconWidth * ICON_ZOOM, + iconHeight * ICON_ZOOM, Image.SCALE_FAST); - ImageIcon imageIcon = new ImageIcon(scaledImage); return imageIcon; } private void prepareLabel(String vertexName, Color vertexColor) { - label.setFont(options.getFont()); + // The label is just used as a renderer and never parented, so no need to be + // on the swing thread + Font font = options.getFont(); + label.setFont(font); label.setText(vertexName); Dimension labelSize = label.getPreferredSize(); - label.setSize(labelSize); + + // make sure the the vertexName doesn't make the icon ridiculously big + int width = Math.min(labelSize.width, MAX_WIDTH); + int height = Math.min(labelSize.height, MAX_HEIGHT); + label.setSize(width, height); } @Override diff --git a/Ghidra/Framework/Graph/src/main/java/ghidra/service/graph/GraphDisplayOptions.java b/Ghidra/Framework/Graph/src/main/java/ghidra/service/graph/GraphDisplayOptions.java index bf87eef867..c8b87337d1 100644 --- a/Ghidra/Framework/Graph/src/main/java/ghidra/service/graph/GraphDisplayOptions.java +++ b/Ghidra/Framework/Graph/src/main/java/ghidra/service/graph/GraphDisplayOptions.java @@ -39,10 +39,10 @@ import ghidra.util.bean.opteditor.OptionsVetoException; * and edge type and shapes for vertex types. */ public class GraphDisplayOptions implements OptionsChangeListener { - + public static final GraphDisplayOptions DEFAULT = new GraphDisplayOptions(new EmptyGraphType()); - + private static final String FONT = "Font"; private static final String LABEL_POSITION = "Label Position"; private static final String USE_ICONS = "Use Icons"; @@ -50,7 +50,7 @@ public class GraphDisplayOptions implements OptionsChangeListener { private static final String EDGE_COLORS = "Edge Colors"; private static final String VERTEX_COLORS = "Vertex Colors"; private static final String VERTEX_SHAPES = "Vertex Shapes"; - private static final String MISCELLANIOUS_OPTIONS = "Miscellanious"; + private static final String MISCELLANEOUS_OPTIONS = "Miscellaneous"; private static final String DEFAULT_VERTEX_COLOR = "Default Vertex Color"; private static final String DEFAULT_EDGE_COLOR = "Default Edge Color"; private static final String DEFAULT_VERTEX_SHAPE = "Default Vertex Shape"; @@ -58,6 +58,8 @@ public class GraphDisplayOptions implements OptionsChangeListener { private static final String VERTEX_SELECTION_COLOR = "Selected Vertex Color"; private static final String EDGE_SELECTION_COLOR = "Selected Edge Color"; + private static final String MAX_NODES_SIZE = "Max Graph Size"; + private GraphType graphType; private Map vertexColorMap = new HashMap<>(); @@ -85,6 +87,8 @@ public class GraphDisplayOptions implements OptionsChangeListener { private Font font = new Font("Dialog", Font.BOLD, 18); private int arrowLength = 15; + private int maxNodeCount = 500; // graph display struggles with too many nodes + /** * Constructs a new GraphTypeDisplayOptions for the given {@link GraphType} * @param graphType The {@link GraphType} for which to define display options @@ -139,8 +143,6 @@ public class GraphDisplayOptions implements OptionsChangeListener { changeListeners.remove(listener); } - - /** * Sets the default shape to be used by vertices that don't have a vertex type set * @param shape the default vertex shape @@ -384,7 +386,6 @@ public class GraphDisplayOptions implements OptionsChangeListener { vertexShapeMap.put(vertexType, Objects.requireNonNull(vertexShape)); } - /** * Returns the color for the given edge type * @param edgeType the edge type whose color is to be determined. @@ -579,6 +580,24 @@ public class GraphDisplayOptions implements OptionsChangeListener { this.arrowLength = length; } + /** + * Returns the maximum number of nodes that can be in a displayed graph + * @return the maximum number of nodes that can be in a displayed graph + */ + public int getMaxNodeCount() { + return maxNodeCount; + } + + /** + * Sets the maximum number of nodes a graph can have and still be displayed. Be careful, + * setting this value too high can result in Ghidra running out of memory and/or + * making the system very sluggish. + * @param maxNodeCount the maximum number of nodes a graph can have and still be displayed. + */ + public void setMaxNodeCount(int maxNodeCount) { + this.maxNodeCount = maxNodeCount; + } + /** * Returns true if this {@link GraphDisplayOptions} instance has been constructed with * a tool for getting/saving option values in the tool options @@ -603,7 +622,7 @@ public class GraphDisplayOptions implements OptionsChangeListener { registerVertexColorOptions(rootOptions, help); registerVertexShapeOptions(rootOptions, help); registerEdgeColorOptions(rootOptions, help); - registerMiscellaniousOptions(rootOptions, help); + registerMiscellaneousOptions(rootOptions, help); } /** @@ -643,7 +662,6 @@ public class GraphDisplayOptions implements OptionsChangeListener { registeredWithTool = true; } - private void updateOptions(Options rootOptions) { updateVertexColorsFromOptions(rootOptions); updateEdgeColorsFromOptions(rootOptions); @@ -652,7 +670,7 @@ public class GraphDisplayOptions implements OptionsChangeListener { } private void updateMiscellaniousOptions(Options rootOptions) { - Options options = rootOptions.getOptions(MISCELLANIOUS_OPTIONS); + Options options = rootOptions.getOptions(MISCELLANEOUS_OPTIONS); String shapeName = options.getString(DEFAULT_VERTEX_SHAPE, defaultVertexShape.getName()); defaultVertexShape = VertexShape.getShape(shapeName); @@ -662,13 +680,14 @@ public class GraphDisplayOptions implements OptionsChangeListener { vertexSelectionColor = options.getColor(VERTEX_SELECTION_COLOR, vertexSelectionColor); edgeSelectionColor = options.getColor(EDGE_SELECTION_COLOR, edgeSelectionColor); - + defaultLayoutAlgorithmName = options.getString(DEFAULT_LAYOUT_ALGORITHM, defaultLayoutAlgorithmName); - + useIcons = options.getBoolean(USE_ICONS, useIcons); labelPosition = options.getEnum(LABEL_POSITION, labelPosition); font = options.getFont(FONT, font); + maxNodeCount = options.getInt(MAX_NODES_SIZE, maxNodeCount); } private void updateVertexShapesFromOptions(Options rootOptions) { @@ -713,7 +732,6 @@ public class GraphDisplayOptions implements OptionsChangeListener { } } - private void registerVertexColorOptions(Options rootOptions, HelpLocation help) { Options options = rootOptions.getOptions(VERTEX_COLORS); @@ -755,10 +773,12 @@ public class GraphDisplayOptions implements OptionsChangeListener { options.registerOptionsEditor(editor); } - private void registerMiscellaniousOptions(Options rootOptions, HelpLocation help) { + private void registerMiscellaneousOptions(Options rootOptions, HelpLocation help) { - Options options = rootOptions.getOptions(MISCELLANIOUS_OPTIONS); + Options options = rootOptions.getOptions(MISCELLANEOUS_OPTIONS); + options.registerOption(MAX_NODES_SIZE, OptionType.INT_TYPE, maxNodeCount, help, + "Graphs with more than this number of nodes will not be displayed. (Large graphs can cause Ghidra to become unstable/sluggish)"); StringWithChoicesEditor editor = new StringWithChoicesEditor(VertexShape.getShapeNames()); options.registerOption(VERTEX_SELECTION_COLOR, OptionType.COLOR_TYPE, vertexSelectionColor, @@ -777,9 +797,12 @@ public class GraphDisplayOptions implements OptionsChangeListener { options.registerOption(DEFAULT_EDGE_COLOR, OptionType.COLOR_TYPE, defaultEdgeColor, help, "Color for edge that have no edge type defined"); - editor = new StringWithChoicesEditor(graphType.getEdgeTypes()); - options.registerOption(FAVORED_EDGE_TYPE, OptionType.STRING_TYPE, favoredEdgeType, help, - "Favored edge is used to influence layout algorithms", editor); + List edgeTypes = graphType.getEdgeTypes(); + if (!edgeTypes.isEmpty()) { + editor = new StringWithChoicesEditor(edgeTypes); + options.registerOption(FAVORED_EDGE_TYPE, OptionType.STRING_TYPE, favoredEdgeType, help, + "Favored edge is used to influence layout algorithms", editor); + } editor = new StringWithChoicesEditor(LayoutAlgorithmNames.getLayoutAlgorithmNames()); options.registerOption(DEFAULT_LAYOUT_ALGORITHM, OptionType.STRING_TYPE, @@ -796,6 +819,7 @@ public class GraphDisplayOptions implements OptionsChangeListener { List optionNamesInDisplayOrder = new ArrayList<>(); + optionNamesInDisplayOrder.add(MAX_NODES_SIZE); optionNamesInDisplayOrder.add(VERTEX_SELECTION_COLOR); optionNamesInDisplayOrder.add(EDGE_SELECTION_COLOR); optionNamesInDisplayOrder.add(DEFAULT_VERTEX_COLOR); @@ -807,9 +831,8 @@ public class GraphDisplayOptions implements OptionsChangeListener { optionNamesInDisplayOrder.add(FONT); optionNamesInDisplayOrder.add(USE_ICONS); - OptionsEditor optionsEditor = - new ScrollableOptionsEditor(MISCELLANIOUS_OPTIONS, optionNamesInDisplayOrder); + new ScrollableOptionsEditor(MISCELLANEOUS_OPTIONS, optionNamesInDisplayOrder); options.registerOptionsEditor(optionsEditor); } @@ -827,5 +850,5 @@ public class GraphDisplayOptions implements OptionsChangeListener { "\" not defined in GraphType \"" + getGraphType().getName() + "\"."); } } -} +} diff --git a/Ghidra/Framework/Graph/src/main/java/ghidra/service/graph/GraphDisplayOptionsBuilder.java b/Ghidra/Framework/Graph/src/main/java/ghidra/service/graph/GraphDisplayOptionsBuilder.java index 809d2f0ee3..3e4965bff9 100644 --- a/Ghidra/Framework/Graph/src/main/java/ghidra/service/graph/GraphDisplayOptionsBuilder.java +++ b/Ghidra/Framework/Graph/src/main/java/ghidra/service/graph/GraphDisplayOptionsBuilder.java @@ -26,7 +26,7 @@ public class GraphDisplayOptionsBuilder { private GraphDisplayOptions displayOptions; /** - * Create a new {@link GraphDisplayOptionsBuilder} + * Create a new GraphDisplayOptionsBuilder * @param graphType the {@link GraphType} of graphs that this instance configures. */ public GraphDisplayOptionsBuilder(GraphType graphType) { @@ -36,7 +36,7 @@ public class GraphDisplayOptionsBuilder { /** * Sets the default vertex color for vertexes that don't have a registered vertex type * @param c the default vertex color - * @return this {@link GraphDisplayOptionsBuilder} + * @return this GraphDisplayOptionsBuilder */ public GraphDisplayOptionsBuilder defaultVertexColor(Color c) { displayOptions.setDefaultVertexColor(c); @@ -46,7 +46,7 @@ public class GraphDisplayOptionsBuilder { /** * Sets the default edge color for edges that don't have a registered edge type * @param c the default edge color - * @return this {@link GraphDisplayOptionsBuilder} + * @return this GraphDisplayOptionsBuilder */ public GraphDisplayOptionsBuilder defaultEdgeColor(Color c) { Objects.requireNonNull(c); @@ -57,7 +57,7 @@ public class GraphDisplayOptionsBuilder { /** * Sets the vertex selection color * @param color the vertex selection color - * @return this {@link GraphDisplayOptionsBuilder} + * @return this GraphDisplayOptionsBuilder */ public GraphDisplayOptionsBuilder vertexSelectionColor(Color color) { displayOptions.setVertexSelectionColor(color); @@ -67,7 +67,7 @@ public class GraphDisplayOptionsBuilder { /** * Sets the edge selection color * @param color the edge selection color - * @return this {@link GraphDisplayOptionsBuilder} + * @return this GraphDisplayOptionsBuilder */ public GraphDisplayOptionsBuilder edgeSelectionColor(Color color) { displayOptions.setEdgeSelectionColor(color); @@ -77,7 +77,7 @@ public class GraphDisplayOptionsBuilder { /** * Sets the default vertex shape for vertices that don't have a registered vertex type * @param vertexShape the {@link VertexShape} to use as a default - * @return this {@link GraphDisplayOptionsBuilder} + * @return this GraphDisplayOptionsBuilder */ public GraphDisplayOptionsBuilder defaultVertexShape(VertexShape vertexShape) { Objects.requireNonNull(vertexShape); @@ -90,7 +90,7 @@ public class GraphDisplayOptionsBuilder { * @param vertexType the vertex type to assign shape and color * @param vertexShape the shape to use for the named vertex type * @param color the color to use for the named vertex type - * @return this {@link GraphDisplayOptionsBuilder} + * @return this GraphDisplayOptionsBuilder */ public GraphDisplayOptionsBuilder vertex(String vertexType, VertexShape vertexShape, Color color) { @@ -102,7 +102,7 @@ public class GraphDisplayOptionsBuilder { * Sets the color for edges of the given type * @param edgeType the edge type to assign color * @param color the color to use for the named edge type - * @return this {@link GraphDisplayOptionsBuilder} + * @return this GraphDisplayOptionsBuilder */ public GraphDisplayOptionsBuilder edge(String edgeType, Color color) { displayOptions.configureEdgeType(edgeType, color); @@ -112,7 +112,7 @@ public class GraphDisplayOptionsBuilder { /** * Sets the attribute used to override the color for a vertex * @param colorAttributeKey the attribute key to use for overriding a vertex color - * @return this {@link GraphDisplayOptionsBuilder} + * @return this GraphDisplayOptionsBuilder */ public GraphDisplayOptionsBuilder vertexColorOverrideAttribute(String colorAttributeKey) { displayOptions.setVertexColorOverrideAttributeKey(colorAttributeKey); @@ -122,7 +122,7 @@ public class GraphDisplayOptionsBuilder { /** * Sets the attribute used to override the color for a edge * @param colorAttributeKey the attribute key to use for overriding an edge color - * @return this {@link GraphDisplayOptionsBuilder} + * @return this GraphDisplayOptionsBuilder */ public GraphDisplayOptionsBuilder edgeColorOverrideAttribute(String colorAttributeKey) { displayOptions.setEdgeColorOverrideAttributeKey(colorAttributeKey); @@ -132,7 +132,7 @@ public class GraphDisplayOptionsBuilder { /** * Sets the attribute used to override the shape for a vertex * @param shapeAttributeKey the attribute key to use of shape override - * @return this {@link GraphDisplayOptionsBuilder} + * @return this GraphDisplayOptionsBuilder */ public GraphDisplayOptionsBuilder shapeOverrideAttribute(String shapeAttributeKey) { displayOptions.setVertexShapeOverrideAttributeKey(shapeAttributeKey); @@ -142,7 +142,7 @@ public class GraphDisplayOptionsBuilder { /** * Sets the name of the layout algorithm that will be used to initially layout the graph * @param string the name of the layout algoritm to use to initially layout the graph - * @return this {@link GraphDisplayOptionsBuilder} + * @return this GraphDisplayOptionsBuilder */ public GraphDisplayOptionsBuilder defaultLayoutAlgorithm(String string) { displayOptions.setDefaultLayoutAlgorithmName(string); @@ -154,7 +154,7 @@ public class GraphDisplayOptionsBuilder { * cached images with the label inside the shapes. If false, vertices are drawn as smaller * shapes with labels drawn near the shapes. * @param b true to use pre-rendered icon images - * @return this {@link GraphDisplayOptionsBuilder} + * @return this GraphDisplayOptionsBuilder */ public GraphDisplayOptionsBuilder useIcons(boolean b) { displayOptions.setUsesIcons(b); @@ -164,18 +164,28 @@ public class GraphDisplayOptionsBuilder { /** * Sets the length of the arrows to display in the graph. The width will be sized proportionately. * @param length the length the arrows to display in the graph - * @return this {@link GraphDisplayOptionsBuilder} + * @return this GraphDisplayOptionsBuilder */ public GraphDisplayOptionsBuilder arrowLength(int length) { displayOptions.setArrowLength(length); return this; } + /** + * Sets the maximum number of nodes a graph can have and still be displayed. + * @param maxNodeCount the maximum number of nodes + * @return this GraphDisplayOptionsBuilder + */ + public GraphDisplayOptionsBuilder maxNodeCount(int maxNodeCount) { + displayOptions.getMaxNodeCount(); + return this; + } + /** * Sets the vertex label position relative to vertex shape. This is only applicable if the - * {@link #useIcons(boolean)} is set to true. + * {@link #useIcons(boolean)} is set to false. * @param labelPosition the relative position to place the vertex label - * @return this {@link GraphDisplayOptionsBuilder} + * @return this GraphDisplayOptionsBuilder */ public GraphDisplayOptionsBuilder labelPosition(GraphLabelPosition labelPosition) { displayOptions.setLabelPosition(labelPosition); diff --git a/Ghidra/Framework/Graph/src/test/java/ghidra/service/graph/GraphDisplayOptionsTest.java b/Ghidra/Framework/Graph/src/test/java/ghidra/service/graph/GraphDisplayOptionsTest.java index 99cfcede06..0c9b5467b3 100644 --- a/Ghidra/Framework/Graph/src/test/java/ghidra/service/graph/GraphDisplayOptionsTest.java +++ b/Ghidra/Framework/Graph/src/test/java/ghidra/service/graph/GraphDisplayOptionsTest.java @@ -37,8 +37,8 @@ public class GraphDisplayOptionsTest { @Before public void setUp() { - List vertexTypes = Arrays.asList("V1", "V2", "V3" ); - List edgeTypes = Arrays.asList("E1", "E2", "E3" ); + List vertexTypes = Arrays.asList("V1", "V2", "V3"); + List edgeTypes = Arrays.asList("E1", "E2", "E3"); graphType = new GraphType("Test", "Test Description", vertexTypes, edgeTypes); options = new GraphDisplayOptions(graphType); } @@ -217,7 +217,6 @@ public class GraphDisplayOptionsTest { assertEquals(VertexShape.STAR, options.getVertexShape("V1")); } - @Test public void testGetEdgeColorForType() { assertEquals(options.getDefaultEdgeColor(), options.getEdgeColor("V1")); @@ -252,8 +251,8 @@ public class GraphDisplayOptionsTest { assertEquals(options.getDefaultEdgeColor(), edgeColorOptions.getColor("E1", Color.WHITE)); - Options miscellaniousOptions = graphDisplayOptions.getOptions("Miscellanious"); - leafOptionNames = miscellaniousOptions.getLeafOptionNames(); + Options miscellaneousOptions = graphDisplayOptions.getOptions("Miscellaneous"); + leafOptionNames = miscellaneousOptions.getLeafOptionNames(); assertEquals(Arrays.asList("Use Icons", "Selected Vertex Color", "Default Layout Algorithm", "Default Vertex Color", "Default Vertex Shape", "Selected Edge Color", "Label Position", "Default Edge Color", "Font", "Favored Edge Type"), leafOptionNames); diff --git a/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/graph/GraphActionsTest.java b/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/graph/GraphActionsTest.java index 218cd5ca68..734744b355 100644 --- a/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/graph/GraphActionsTest.java +++ b/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/graph/GraphActionsTest.java @@ -33,6 +33,7 @@ import ghidra.graph.visualization.GroupVertex; import ghidra.service.graph.*; import ghidra.test.AbstractGhidraHeadedIntegrationTest; import ghidra.test.TestEnv; +import ghidra.util.exception.CancelledException; import ghidra.util.task.TaskMonitor; public class GraphActionsTest extends AbstractGhidraHeadedIntegrationTest { @@ -445,7 +446,18 @@ public class GraphActionsTest extends AbstractGhidraHeadedIntegrationTest { GraphDisplayBroker broker = tool.getService(GraphDisplayBroker.class); GraphDisplayProvider service = broker.getGraphDisplayProvider("Default Graph Display"); display = service.getGraphDisplay(false, TaskMonitor.DUMMY); - display.setGraph(graph, "test graph", false, TaskMonitor.DUMMY); + GraphDisplayOptions options = new GraphDisplayOptions(graph.getGraphType()); + + runSwing(() -> { + + try { + display.setGraph(graph, options, "test graph", false, TaskMonitor.DUMMY); + } + catch (CancelledException e) { + // can't happen with a dummy monitor + } + }); + display.setGraphDisplayListener(new TestGraphDisplayListener("test")); }