From b03b5e112cbd5513a6e03bfcacc867e33e65799b Mon Sep 17 00:00:00 2001 From: dragonmacher <48328597+dragonmacher@users.noreply.github.com> Date: Sat, 24 May 2025 16:54:55 -0400 Subject: [PATCH] Graph - Refactored the FcgVertex to create a generic circle vertex shape provider --- .../Features/Base/data/base.theme.properties | 2 + .../base/graph/CircleWithLabelVertex.java | 64 +++ .../CircleWithLabelVertexShapeProvider.java | 359 ++++++++++++ .../base/graph/VertexExpansionListener.java | 38 ++ .../data/functioncallgraph.theme.properties | 3 +- .../java/functioncalls/graph/FcgVertex.java | 531 +++--------------- .../graph/FcgVertexShapeProvider.java | 338 +++++++++++ .../graph/view/FcgComponent.java | 8 +- .../viewer/vertex/AbstractVisualVertex.java | 5 +- 9 files changed, 882 insertions(+), 466 deletions(-) create mode 100644 Ghidra/Features/Base/src/main/java/ghidra/base/graph/CircleWithLabelVertex.java create mode 100644 Ghidra/Features/Base/src/main/java/ghidra/base/graph/CircleWithLabelVertexShapeProvider.java create mode 100644 Ghidra/Features/Base/src/main/java/ghidra/base/graph/VertexExpansionListener.java create mode 100644 Ghidra/Features/GraphFunctionCalls/src/main/java/functioncalls/graph/FcgVertexShapeProvider.java diff --git a/Ghidra/Features/Base/data/base.theme.properties b/Ghidra/Features/Base/data/base.theme.properties index 6cee9360df..0d04c30b02 100644 --- a/Ghidra/Features/Base/data/base.theme.properties +++ b/Ghidra/Features/Base/data/base.theme.properties @@ -26,6 +26,8 @@ color.fg.dialog.equates.suggestion = color.palette.hint color.fg.consoletextpane = color.fg color.fg.consoletextpane.error = color.fg.error +color.bg.graph.vertex.function = MediumAquamarine + color.fg.infopanel.version = color.fg color.bg.interpreterconsole = color.bg diff --git a/Ghidra/Features/Base/src/main/java/ghidra/base/graph/CircleWithLabelVertex.java b/Ghidra/Features/Base/src/main/java/ghidra/base/graph/CircleWithLabelVertex.java new file mode 100644 index 0000000000..19dece4a51 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/base/graph/CircleWithLabelVertex.java @@ -0,0 +1,64 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.base.graph; + +import java.awt.Shape; + +import javax.swing.JComponent; + +import ghidra.graph.viewer.vertex.AbstractVisualVertex; +import ghidra.graph.viewer.vertex.VertexShapeProvider; + +/** + * A vertex that is a circle shape with a label below the circle to show the given text. + */ +public class CircleWithLabelVertex extends AbstractVisualVertex implements VertexShapeProvider { + + protected CircleWithLabelVertexShapeProvider shapeProvider; + + public CircleWithLabelVertex(String label) { + this.shapeProvider = new CircleWithLabelVertexShapeProvider(label); + } + + @Override + public JComponent getComponent() { + return shapeProvider.getComponent(); + } + + @Override + public Shape getCompactShape() { + return shapeProvider.getCompactShape(); + } + + @Override + public Shape getFullShape() { + return shapeProvider.getFullShape(); + } + + public String getName() { + return shapeProvider.getName(); + } + + @Override + public String toString() { + return shapeProvider.getName();// + " @ " + level; // + " (" + System.identityHashCode(this) + ')'; + } + + @Override + public void dispose() { + // nothing to do + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/base/graph/CircleWithLabelVertexShapeProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/base/graph/CircleWithLabelVertexShapeProvider.java new file mode 100644 index 0000000000..743f039f7d --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/base/graph/CircleWithLabelVertexShapeProvider.java @@ -0,0 +1,359 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.base.graph; + +import java.awt.*; +import java.awt.geom.Area; +import java.awt.geom.Ellipse2D; +import java.awt.geom.Ellipse2D.Double; +import java.awt.image.BufferedImage; + +import javax.swing.*; +import javax.swing.border.Border; +import javax.swing.border.LineBorder; + +import docking.widgets.EmptyBorderButton; +import docking.widgets.label.GDLabel; +import generic.theme.GColor; +import generic.theme.GThemeDefaults.Colors.Palette; +import ghidra.graph.viewer.vertex.VertexShapeProvider; +import resources.Icons; +import resources.ResourceManager; + +public class CircleWithLabelVertexShapeProvider implements VertexShapeProvider { + + //@formatter:off + public static final Color DEFAULT_VERTEX_SHAPE_COLOR = new GColor("color.bg.graph.vertex.function"); + //@formatter:on + + protected static final Icon EXPAND_ICON = + ResourceManager.getScaledIcon(Icons.EXPAND_ALL_ICON, 10, 10); + protected static final Icon COLLAPSE_ICON = + ResourceManager.getScaledIcon(Icons.COLLAPSE_ALL_ICON, 10, 10); + + // higher numbered layers go on top + protected static final Integer VERTEX_SHAPE_LAYER = 100; + protected static final Integer TOGGLE_BUTTON_LAYER = 200; + protected static final Integer LABEL_LAYER = 300; + + protected static final int GAP = 2; + protected static final int VERTEX_SHAPE_SIZE = 50; + + // Note: This should be made into an option + // based upon the default function name, plus some extra + protected static final int MAX_NAME_LENGTH = 30; + + protected JLayeredPane layeredPane; + protected JButton toggleInsButton = new EmptyBorderButton(EXPAND_ICON); + protected JButton toggleOutsButton = new EmptyBorderButton(EXPAND_ICON); + protected JLabel nameLabel = new GDLabel(); + protected JLabel vertexImageLabel = new GDLabel(); + + protected Double vertexShape; + protected Double compactShape; + protected Shape fullShape; + + protected boolean incomingExpanded; + protected boolean outgoingExpanded; + + // set this to true to see borders around the components of this vertex + protected boolean useDebugBorders = false; + + private String fullLabelText; + + public CircleWithLabelVertexShapeProvider(String label) { + this.fullLabelText = label; + buildUi(); + } + + public CircleWithLabelVertexShapeProvider(String label, + VertexExpansionListener expansionListener) { + this.fullLabelText = label; + buildUi(); + } + + protected void buildUi() { + + String name = generateLabelText(); + nameLabel.setText(name); + buildVertexShape(); + + // calculate the needed size + layeredPane = new JLayeredPane(); + Border border = createDebugBorder(new LineBorder(Palette.GOLD, 1)); + layeredPane.setBorder(border); + + updateLayeredPaneSize(); + + // layout the components + addVertexShape(); + addToggleButtons(); + addNameLabel(); + + buildFullShape(); + } + + protected String generateLabelText() { + return fullLabelText; + } + + private Border createDebugBorder(Border border) { + if (useDebugBorders) { + return border; + } + return BorderFactory.createEmptyBorder(); + } + + private void buildFullShape() { + + // Note: this method assumes all bounds have been set + Area parent = new Area(); + + Area v = new Area(vertexShape); + Area name = new Area(nameLabel.getBounds()); + parent.add(v); + parent.add(name); + + // for now, the buttons only appear on hover, but if we want to avoid clipping when + // painting, we need to account for them in the shape's overall bounds + Area in = new Area(toggleInsButton.getBounds()); + Area out = new Area(toggleOutsButton.getBounds()); + parent.add(in); + parent.add(out); + + fullShape = parent; + } + + private void updateLayeredPaneSize() { + + // + // The overall component size is the total width and height of all components, with any + // spacing between them. + // + + Dimension shapeSize = vertexImageLabel.getPreferredSize(); + Dimension nameLabelSize = nameLabel.getPreferredSize(); + int height = shapeSize.height + GAP + nameLabelSize.height; + + Dimension insSize = toggleInsButton.getPreferredSize(); + Dimension outsSize = toggleOutsButton.getPreferredSize(); + int buttonWidth = Math.max(insSize.width, outsSize.width); + int offset = buttonWidth / 3; // overlap the vertex shape + + int width = offset + shapeSize.width; + width = Math.max(width, nameLabelSize.width); + + layeredPane.setPreferredSize(new Dimension(width, height)); + } + + private void buildVertexShape() { + int w = VERTEX_SHAPE_SIZE; + int h = VERTEX_SHAPE_SIZE; + Double circle = new Ellipse2D.Double(0, 0, w, h); + + BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); + Graphics2D g2 = (Graphics2D) image.getGraphics(); + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + g2.setPaint(getVertexShapePaint()); + + g2.fill(circle); + + g2.dispose(); + + vertexShape = circle; + compactShape = (Double) vertexShape.clone(); + vertexImageLabel.setIcon(new ImageIcon(image)); + + Border border = createDebugBorder(new LineBorder(Palette.PINK, 1)); + vertexImageLabel.setBorder(border); + } + + protected Paint getVertexShapePaint() { + return getDefaultVertexShapeColor(); + } + + protected Color getDefaultVertexShapeColor() { + return DEFAULT_VERTEX_SHAPE_COLOR; + } + + private void addVertexShape() { + + Dimension parentSize = layeredPane.getPreferredSize(); + Dimension size = vertexImageLabel.getPreferredSize(); + + // centered + int x = (parentSize.width / 2) - (size.width / 2); + int y = 0; + + vertexImageLabel.setBounds(x, y, size.width, size.height); + Dimension shapeSize = vertexShape.getBounds().getSize(); + + // setFrame() will make sure the shape's x,y values are where they need to be + // for the later 'full shape' creation + vertexShape.setFrame(x, y, shapeSize.width, shapeSize.height); + layeredPane.add(vertexImageLabel, VERTEX_SHAPE_LAYER); + } + + private void addNameLabel() { + + Border border = createDebugBorder(new LineBorder(Palette.GREEN, 1)); + nameLabel.setBorder(border); + + // assume the vertex label has been bounded + Rectangle parentBounds = vertexImageLabel.getBounds(); + Dimension size = nameLabel.getPreferredSize(); + + // bottom, centered under the shape + int x = (parentBounds.x + (parentBounds.width / 2)) - (size.width / 2); + int y = parentBounds.y + parentBounds.height + GAP; + nameLabel.setBounds(x, y, size.width, size.height); + layeredPane.add(nameLabel, LABEL_LAYER); + } + + private void addToggleButtons() { + + // hide the button background + toggleInsButton.setBackground(Palette.NO_COLOR); + toggleOutsButton.setBackground(Palette.NO_COLOR); + + // This is needed for Flat Dark theme to work correctly, due to the fact that it wants to + // paint its parent background when the button is opaque. The parent background will get + // painted over any items that lie between the button and the parent. + toggleInsButton.setOpaque(false); + toggleOutsButton.setOpaque(false); + + Rectangle parentBounds = vertexImageLabel.getBounds(); + Dimension size = toggleInsButton.getPreferredSize(); + + // upper toggle; upper-left + int x = parentBounds.x - (size.width / 3); + int y = 0; + toggleInsButton.setBounds(x, y, size.width, size.height); + layeredPane.add(toggleInsButton, TOGGLE_BUTTON_LAYER); + + // lower toggle; lower-left, lined-up with the vertex shape + size = toggleOutsButton.getPreferredSize(); + Dimension vertexSize = parentBounds.getSize(); + y = vertexSize.height - size.height; + toggleOutsButton.setBounds(x, y, size.width, size.height); + layeredPane.add(toggleOutsButton, TOGGLE_BUTTON_LAYER); + } + + public String getName() { + return fullLabelText; + } + + public JButton getIncomingToggleButton() { + return toggleInsButton; + } + + public JButton getOutgoingToggleButton() { + return toggleOutsButton; + } + + /** + * Sets to true if this vertex is showing all edges in the incoming direction + * + * @param setExpanded true if this vertex is showing all edges in the incoming direction + */ + public void setIncomingExpanded(boolean setExpanded) { + this.incomingExpanded = setExpanded; + toggleInsButton.setIcon(setExpanded ? COLLAPSE_ICON : EXPAND_ICON); + String hideShow = setExpanded ? "hide" : "show"; + toggleInsButton.setToolTipText("Click to " + hideShow + " incoming edges"); + } + + /** + * Returns true if this vertex is showing all edges in the incoming direction + * + * @return true if this vertex is showing all edges in the incoming direction + */ + public boolean isIncomingExpanded() { + return incomingExpanded; + } + + /** + * Sets to true if this vertex is showing all edges in the outgoing direction + * + * @param setExpanded true if this vertex is showing all edges in the outgoing direction + */ + public void setOutgoingExpanded(boolean setExpanded) { + this.outgoingExpanded = setExpanded; + toggleOutsButton.setIcon(setExpanded ? COLLAPSE_ICON : EXPAND_ICON); + String hideShow = setExpanded ? "hide" : "show"; + toggleInsButton.setToolTipText("Click to " + hideShow + " outgoing edges"); + } + + /** + * Returns true if this vertex is showing all edges in the outgoing direction + * + * @return true if this vertex is showing all edges in the outgoing direction + */ + public boolean isOutgoingExpanded() { + return outgoingExpanded; + } + + /** + * Returns whether this vertex is fully expanded in its current direction + * + * @return whether this vertex is fully expanded in its current direction + */ + public boolean isExpanded() { + return isIncomingExpanded() && isOutgoingExpanded(); + } + + /** + * Returns true if this node can be expanded + * @return true if this node can be expanded + */ + public boolean canExpand() { + return false; // subclasses can override + } + + protected boolean hasIncomingEdges() { + return false; + } + + protected boolean hasOutgoingEdges() { + return false; + } + + protected void setTogglesVisible(boolean visible) { + toggleInsButton.setVisible(visible); + toggleOutsButton.setVisible(visible); + } + + public JComponent getComponent() { + return layeredPane; + } + + @Override + public Shape getCompactShape() { + return compactShape; + } + + @Override + public Shape getFullShape() { + return fullShape; + } + + @Override + public String toString() { + return getName();// + " @ " + level; // + " (" + System.identityHashCode(this) + ')'; + } + +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/base/graph/VertexExpansionListener.java b/Ghidra/Features/Base/src/main/java/ghidra/base/graph/VertexExpansionListener.java new file mode 100644 index 0000000000..d531a9f932 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/base/graph/VertexExpansionListener.java @@ -0,0 +1,38 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.base.graph; + +import ghidra.graph.viewer.VisualVertex; + +/** + * A listener to know when a vertex has been told to expand + */ +public interface VertexExpansionListener { + + /** + * Show or hide those vertices that are on incoming edges to v + * + * @param v the vertex + */ + public void toggleIncomingVertices(VisualVertex v); + + /** + * Show or hide those vertices that are on outgoing edges to v + * + * @param v the vertex + */ + public void toggleOutgoingVertices(VisualVertex v); +} diff --git a/Ghidra/Features/GraphFunctionCalls/data/functioncallgraph.theme.properties b/Ghidra/Features/GraphFunctionCalls/data/functioncallgraph.theme.properties index 811912f2d8..c2a6a6c985 100644 --- a/Ghidra/Features/GraphFunctionCalls/data/functioncallgraph.theme.properties +++ b/Ghidra/Features/GraphFunctionCalls/data/functioncallgraph.theme.properties @@ -1,9 +1,8 @@ [Defaults] -color.bg.plugin.fcg.vertex.default = MediumAquamarine // TODO color.bg.plugin.fcg.vertex.toobig = color.palette.lightgray -color.bg.plugin.fcg.edge.primary.direct = darkseagreen // TODO +color.bg.plugin.fcg.edge.primary.direct = darkseagreen color.bg.plugin.fcg.edge.primary.direct.selected = color.palette.lightgreen color.bg.plugin.fcg.edge.primary.indirect = color.palette.lavender color.bg.plugin.fcg.edge.primary.indirect.selected = color.palette.silver diff --git a/Ghidra/Features/GraphFunctionCalls/src/main/java/functioncalls/graph/FcgVertex.java b/Ghidra/Features/GraphFunctionCalls/src/main/java/functioncalls/graph/FcgVertex.java index a93e17bba4..ba9d101515 100644 --- a/Ghidra/Features/GraphFunctionCalls/src/main/java/functioncalls/graph/FcgVertex.java +++ b/Ghidra/Features/GraphFunctionCalls/src/main/java/functioncalls/graph/FcgVertex.java @@ -15,88 +15,26 @@ */ package functioncalls.graph; -import java.awt.*; -import java.awt.geom.Area; -import java.awt.geom.Ellipse2D; -import java.awt.geom.Ellipse2D.Double; -import java.awt.image.BufferedImage; import java.util.Objects; -import javax.swing.*; -import javax.swing.border.Border; -import javax.swing.border.LineBorder; +import javax.swing.JButton; -import docking.widgets.EmptyBorderButton; -import docking.widgets.label.GDLabel; import functioncalls.plugin.FcgOptions; -import generic.theme.GColor; -import generic.theme.GThemeDefaults.Colors.Palette; -import generic.theme.Gui; -import ghidra.graph.viewer.vertex.AbstractVisualVertex; -import ghidra.graph.viewer.vertex.VertexShapeProvider; +import ghidra.base.graph.CircleWithLabelVertex; import ghidra.program.model.address.Address; import ghidra.program.model.listing.Function; -import ghidra.util.StringUtilities; -import resources.Icons; -import resources.ResourceManager; /** * A {@link FunctionCallGraph} vertex */ -public class FcgVertex extends AbstractVisualVertex implements VertexShapeProvider { - - //@formatter:off - public static final Color DEFAULT_VERTEX_SHAPE_COLOR = new GColor("color.bg.plugin.fcg.vertex.default"); - private static final Color TOO_BIG_VERTEX_SHAPE_COLOR = new GColor("color.bg.plugin.fcg.vertex.toobig"); - //@formatter:on - - public static final Icon NOT_ALLOWED_ICON = Icons.ERROR_ICON; - private static final Icon EXPAND_ICON = - ResourceManager.getScaledIcon(Icons.EXPAND_ALL_ICON, 10, 10); - private static final Icon COLLAPSE_ICON = - ResourceManager.getScaledIcon(Icons.COLLAPSE_ALL_ICON, 10, 10); - - // higher numbered layers go on top - private static final Integer VERTEX_SHAPE_LAYER = 100; - private static final Integer TOGGLE_BUTTON_LAYER = 200; - private static final Integer LABEL_LAYER = 300; - - private static final int GAP = 2; - private static final int VERTEX_SHAPE_SIZE = 50; - - // TODO to be made an option in an upcoming ticket - // based upon the default function name, plus some extra - private static final int MAX_NAME_LENGTH = 30; +public class FcgVertex extends CircleWithLabelVertex { private Function function; - - private JLayeredPane layeredPane; - private JButton toggleInsButton = new EmptyBorderButton(EXPAND_ICON); - private JButton toggleOutsButton = new EmptyBorderButton(EXPAND_ICON); - private JLabel nameLabel = new GDLabel(); - private JLabel vertexImageLabel = new GDLabel(); - - private Double vertexShape; - private Double compactShape; - private Shape fullShape; - - // these values are set after construction from external sources - private boolean hasIncomingReferences; - private boolean hasOutgoingReferences; - private boolean tooManyIncomingReferences; - private boolean tooManyOutgoingReferences; - private boolean incomingExpanded; - private boolean outgoingExpanded; - - // set this to true to see borders around the components of this vertex - private boolean useDebugBorders = false; - - private Paint inPaint; - private Paint outPaint; - private FcgLevel level; private FcgOptions options; + private FcgVertexShapeProvider fcgShapeProvider; + /** * Constructor * @@ -107,252 +45,14 @@ public class FcgVertex extends AbstractVisualVertex implements VertexShapeProvid */ public FcgVertex(Function function, FcgLevel level, FcgVertexExpansionListener expansionListener, FcgOptions options) { + super(function.getName()); this.function = function; this.level = level; this.options = options; - Objects.requireNonNull(expansionListener); - toggleInsButton.addActionListener(e -> { - if (tooManyIncomingReferences) { - return; - } - expansionListener.toggleIncomingVertices(FcgVertex.this); - }); + fcgShapeProvider = new FcgVertexShapeProvider(this, expansionListener); + shapeProvider = fcgShapeProvider; - toggleOutsButton.addActionListener(e -> { - if (tooManyOutgoingReferences) { - return; - } - expansionListener.toggleOutgoingVertices(FcgVertex.this); - }); - - buildUi(); - - setTogglesVisible(false); - } - - private void createPaints() { - - Color vertexShapeColor = getVertexShapeColor(); - - Color lightColor = vertexShapeColor; - Color darkColor = Gui.darker(vertexShapeColor); - Color darkestColor = Gui.darker(darkColor); - int offset = 5 * level.getDistance(); - int half = VERTEX_SHAPE_SIZE / 2; - int start = 0; - int end = half + offset; - - // paint top-down: dark to light for incoming; light to dark for outgoing - inPaint = new LinearGradientPaint(new Point(0, start), new Point(0, end), - new float[] { .0f, .2f, 1f }, new Color[] { darkestColor, darkColor, lightColor }); - - start = half - offset; // (offset + 10); - end = VERTEX_SHAPE_SIZE; - outPaint = new LinearGradientPaint(new Point(0, start), new Point(0, end), - new float[] { .0f, .8f, 1f }, new Color[] { lightColor, darkColor, darkestColor }); - } - - private void buildUi() { - - createPaints(); - - // init the components - boolean truncate = options.useTruncatedFunctionNames(); - String name = getName(); - if (truncate) { - name = StringUtilities.trimMiddle(getName(), MAX_NAME_LENGTH); - } - - nameLabel.setText(name); - buildVertexShape(); - - // calculate the needed size - layeredPane = new JLayeredPane(); - Border border = createDebugBorder(new LineBorder(Palette.GOLD, 1)); - layeredPane.setBorder(border); - - updateLayeredPaneSize(); - - // layout the components - addVertexShape(); - addToggleButtons(); - addNameLabel(); - - buildFullShape(); - } - - private Border createDebugBorder(Border border) { - if (useDebugBorders) { - return border; - } - return BorderFactory.createEmptyBorder(); - } - - private void buildFullShape() { - - // Note: this method assumes all bounds have been set - Area parent = new Area(); - - Area v = new Area(vertexShape); - Area name = new Area(nameLabel.getBounds()); - parent.add(v); - parent.add(name); - - // for now, the buttons only appear on hover, but if we want to avoid clipping when - // painting, we need to account for them in the shape's overall bounds - Area in = new Area(toggleInsButton.getBounds()); - Area out = new Area(toggleOutsButton.getBounds()); - parent.add(in); - parent.add(out); - - fullShape = parent; - } - - private void updateLayeredPaneSize() { - - // - // The overall component size is the total width and height of all components, with any - // spacing between them. - // - - Dimension shapeSize = vertexImageLabel.getPreferredSize(); - Dimension nameLabelSize = nameLabel.getPreferredSize(); - int height = shapeSize.height + GAP + nameLabelSize.height; - - Dimension insSize = toggleInsButton.getPreferredSize(); - Dimension outsSize = toggleOutsButton.getPreferredSize(); - int buttonWidth = Math.max(insSize.width, outsSize.width); - int offset = buttonWidth / 3; // overlap the vertex shape - - int width = offset + shapeSize.width; - width = Math.max(width, nameLabelSize.width); - - layeredPane.setPreferredSize(new Dimension(width, height)); - } - - private void buildVertexShape() { - int w = VERTEX_SHAPE_SIZE; - int h = VERTEX_SHAPE_SIZE; - Double circle = new Ellipse2D.Double(0, 0, w, h); - - BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); - Graphics2D g2 = (Graphics2D) image.getGraphics(); - g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - - FcgDirection direction = level.getDirection(); - if (direction.isSource()) { - g2.setColor(getVertexShapeColor()); - } - else if (direction.isIn()) { - g2.setPaint(inPaint); - } - else { - g2.setPaint(outPaint); - } - - g2.fill(circle); - - g2.dispose(); - - vertexShape = circle; - compactShape = (Double) vertexShape.clone(); - vertexImageLabel.setIcon(new ImageIcon(image)); - - Border border = createDebugBorder(new LineBorder(Palette.PINK, 1)); - vertexImageLabel.setBorder(border); - } - - private Color getVertexShapeColor() { - - if (isInDirection() && tooManyIncomingReferences) { - return TOO_BIG_VERTEX_SHAPE_COLOR; - } - - if (isOutDirection() && tooManyOutgoingReferences) { - return TOO_BIG_VERTEX_SHAPE_COLOR; - } - - return DEFAULT_VERTEX_SHAPE_COLOR; - } - - private boolean isInDirection() { - FcgDirection direction = level.getDirection(); - boolean isIn = direction.isIn() || direction.isSource(); - return isIn; - } - - private boolean isOutDirection() { - FcgDirection direction = level.getDirection(); - boolean isOut = direction.isOut() || direction.isSource(); - return isOut; - } - - private void addVertexShape() { - - Dimension parentSize = layeredPane.getPreferredSize(); - Dimension size = vertexImageLabel.getPreferredSize(); - - // centered - int x = (parentSize.width / 2) - (size.width / 2); - int y = 0; - - vertexImageLabel.setBounds(x, y, size.width, size.height); - Dimension shapeSize = vertexShape.getBounds().getSize(); - - // setFrame() will make sure the shape's x,y values are where they need to be - // for the later 'full shape' creation - vertexShape.setFrame(x, y, shapeSize.width, shapeSize.height); - layeredPane.add(vertexImageLabel, VERTEX_SHAPE_LAYER); - } - - private void addNameLabel() { - - Border border = createDebugBorder(new LineBorder(Palette.GREEN, 1)); - nameLabel.setBorder(border); - - // assume the vertex label has been bounded - Rectangle parentBounds = vertexImageLabel.getBounds(); - Dimension size = nameLabel.getPreferredSize(); - - // bottom, centered under the shape - int x = (parentBounds.x + (parentBounds.width / 2)) - (size.width / 2); - int y = parentBounds.y + parentBounds.height + GAP; - nameLabel.setBounds(x, y, size.width, size.height); - layeredPane.add(nameLabel, LABEL_LAYER); - } - - private void addToggleButtons() { - - // hide the button background - toggleInsButton.setBackground(Palette.NO_COLOR); - toggleOutsButton.setBackground(Palette.NO_COLOR); - - // This is needed for Flat Dark theme to work correctly, due to the fact that it wants to - // paint its parent background when the button is opaque. The parent background will get - // painted over any items that lie between the button and the parent. - toggleInsButton.setOpaque(false); - toggleOutsButton.setOpaque(false); - - Rectangle parentBounds = vertexImageLabel.getBounds(); - Dimension size = toggleInsButton.getPreferredSize(); - - // upper toggle; upper-left - int x = parentBounds.x - (size.width / 3); - int y = 0; - toggleInsButton.setBounds(x, y, size.width, size.height); - layeredPane.add(toggleInsButton, TOGGLE_BUTTON_LAYER); - - // lower toggle; lower-left, lined-up with the vertex shape - size = toggleOutsButton.getPreferredSize(); - Dimension vertexSize = parentBounds.getSize(); - y = vertexSize.height - size.height; - toggleOutsButton.setBounds(x, y, size.width, size.height); - layeredPane.add(toggleOutsButton, TOGGLE_BUTTON_LAYER); - } - - public String getName() { - return function.getName(); } public Function getFunction() { @@ -363,6 +63,10 @@ public class FcgVertex extends AbstractVisualVertex implements VertexShapeProvid return function.getEntryPoint(); } + public FcgOptions getOptions() { + return options; + } + public FcgLevel getLevel() { return level; } @@ -375,105 +79,35 @@ public class FcgVertex extends AbstractVisualVertex implements VertexShapeProvid return level.getDirection(); } + @Override + public void setHovered(boolean hovered) { + super.setHovered(hovered); + fcgShapeProvider.setTogglesVisible(hovered); + } + public JButton getIncomingToggleButton() { - return toggleInsButton; + return shapeProvider.getIncomingToggleButton(); } public JButton getOutgoingToggleButton() { - return toggleOutsButton; + return shapeProvider.getOutgoingToggleButton(); } /** - * Sets to true if this vertex is showing all edges in the incoming direction + * Sets whether this vertex has any incoming references * - * @param setExpanded true if this vertex is showing all edges in the incoming direction + * @param hasIncoming true if this vertex has any incoming references */ - public void setIncomingExpanded(boolean setExpanded) { - - validateIncomingExpandedState(setExpanded); - - this.incomingExpanded = setExpanded; - toggleInsButton.setIcon(setExpanded ? COLLAPSE_ICON : EXPAND_ICON); - String hideShow = setExpanded ? "hide" : "show"; - toggleInsButton.setToolTipText("Click to " + hideShow + " incoming edges"); - } - - private void validateOutgoingExpandedState(boolean isExpanding) { - if (isExpanding) { - if (!canExpandOutgoingReferences()) { - throw new IllegalStateException("Vertex cannot be expanded: " + this); - } - return; - } - - // collapsing - if (!isOutgoingExpanded()) { - throw new IllegalStateException("Vertex cannot be collapsed: " + this); - } - } - - private void validateIncomingExpandedState(boolean expanding) { - - if (expanding) { - if (!canExpandIncomingReferences()) { - throw new IllegalStateException("Vertex cannot be expanded: " + this); - } - return; - } - - // collapsing - if (!isIncomingExpanded()) { - throw new IllegalStateException("Vertex cannot be collapsed: " + this); - } + public void setHasIncomingReferences(boolean hasIncoming) { + fcgShapeProvider.setHasIncomingReferences(hasIncoming); } /** - * Returns true if this vertex is showing all edges in the incoming direction - * - * @return true if this vertex is showing all edges in the incoming direction + * Sets whether this vertex has any outgoing references + * @param hasOutgoing true if this vertex has any outgoing references */ - public boolean isIncomingExpanded() { - return incomingExpanded; - } - - /** - * Sets to true if this vertex is showing all edges in the outgoing direction - * - * @param setExpanded true if this vertex is showing all edges in the outgoing direction - */ - public void setOutgoingExpanded(boolean setExpanded) { - - validateOutgoingExpandedState(setExpanded); - - this.outgoingExpanded = setExpanded; - toggleOutsButton.setIcon(setExpanded ? COLLAPSE_ICON : EXPAND_ICON); - String hideShow = setExpanded ? "hide" : "show"; - toggleInsButton.setToolTipText("Click to " + hideShow + " outgoing edges"); - } - - /** - * Returns true if this vertex is showing all edges in the outgoing direction - * - * @return true if this vertex is showing all edges in the outgoing direction - */ - public boolean isOutgoingExpanded() { - return outgoingExpanded; - } - - /** - * Returns whether this vertex is fully expanded in its current direction - * - * @return whether this vertex is fully expanded in its current direction - */ - public boolean isExpanded() { - FcgDirection direction = level.getDirection(); - if (direction.isSource()) { - return isIncomingExpanded() && isOutgoingExpanded(); - } - if (direction.isIn()) { - return isIncomingExpanded(); - } - return isOutgoingExpanded(); + public void setHasOutgoingReferences(boolean hasOutgoing) { + fcgShapeProvider.setHasOutgoingReferences(hasOutgoing); } /** @@ -484,10 +118,7 @@ public class FcgVertex extends AbstractVisualVertex implements VertexShapeProvid * @param tooMany if there are too many references */ public void setTooManyIncomingReferences(boolean tooMany) { - this.tooManyIncomingReferences = tooMany; - toggleInsButton.setIcon(NOT_ALLOWED_ICON); - toggleInsButton.setToolTipText("Too many incoming references to show"); - buildUi(); + fcgShapeProvider.setTooManyIncomingReferences(tooMany); } /** @@ -498,10 +129,7 @@ public class FcgVertex extends AbstractVisualVertex implements VertexShapeProvid * @param tooMany if there are too many references */ public void setTooManyOutgoingReferences(boolean tooMany) { - this.tooManyOutgoingReferences = tooMany; - toggleOutsButton.setIcon(NOT_ALLOWED_ICON); - toggleOutsButton.setToolTipText("Too many outgoing references to show"); - buildUi(); + fcgShapeProvider.setTooManyOutgoingReferences(tooMany); } /** @@ -512,7 +140,7 @@ public class FcgVertex extends AbstractVisualVertex implements VertexShapeProvid * @return true if there are too many references */ public boolean hasTooManyIncomingReferences() { - return tooManyIncomingReferences; + return fcgShapeProvider.hasTooManyIncomingReferences(); } /** @@ -523,7 +151,34 @@ public class FcgVertex extends AbstractVisualVertex implements VertexShapeProvid * @return true if there are too many references */ public boolean hasTooManyOutgoingReferences() { - return tooManyOutgoingReferences; + return fcgShapeProvider.hasTooManyOutgoingReferences(); + } + + /** + * Returns true if this vertex is showing all edges in the incoming direction + * + * @return true if this vertex is showing all edges in the incoming direction + */ + public boolean isIncomingExpanded() { + return fcgShapeProvider.isIncomingExpanded(); + } + + /** + * Returns true if this vertex is showing all edges in the outgoing direction + * + * @return true if this vertex is showing all edges in the outgoing direction + */ + public boolean isOutgoingExpanded() { + return fcgShapeProvider.isOutgoingExpanded(); + } + + /** + * Returns whether this vertex is fully expanded in its current direction + * + * @return whether this vertex is fully expanded in its current direction + */ + public boolean isExpanded() { + return fcgShapeProvider.isExpanded(); } /** @@ -533,76 +188,33 @@ public class FcgVertex extends AbstractVisualVertex implements VertexShapeProvid * @return true if this vertex can be expanded */ public boolean canExpand() { - FcgDirection direction = level.getDirection(); - if (direction.isSource()) { - return canExpandIncomingReferences() || canExpandOutgoingReferences(); - } - - if (direction.isIn()) { - return canExpandIncomingReferences(); - } - - return canExpandOutgoingReferences(); + return fcgShapeProvider.canExpand(); } public boolean canExpandIncomingReferences() { - return hasIncomingReferences && !tooManyIncomingReferences && !incomingExpanded; + return fcgShapeProvider.canExpandIncomingReferences(); } public boolean canExpandOutgoingReferences() { - return hasOutgoingReferences && !tooManyOutgoingReferences && !outgoingExpanded; + return fcgShapeProvider.canExpandOutgoingReferences(); } /** - * Sets whether this vertex has any incoming references + * Sets to true if this vertex is showing all edges in the incoming direction * - * @param hasIncoming true if this vertex has any incoming references + * @param setExpanded true if this vertex is showing all edges in the incoming direction */ - public void setHasIncomingReferences(boolean hasIncoming) { - this.hasIncomingReferences = hasIncoming; + public void setIncomingExpanded(boolean setExpanded) { + fcgShapeProvider.setIncomingExpanded(setExpanded); } /** - * Sets whether this vertex has any outgoing references + * Sets to true if this vertex is showing all edges in the outgoing direction * - * @param hasOutgoing true if this vertex has any outgoing references + * @param setExpanded true if this vertex is showing all edges in the outgoing direction */ - - public void setHasOutgoingReferences(boolean hasOutgoing) { - this.hasOutgoingReferences = hasOutgoing; - } - - @Override - public void setHovered(boolean hovered) { - super.setHovered(hovered); - - setTogglesVisible(hovered); - } - - private void setTogglesVisible(boolean visible) { - - boolean isIn = isInDirection(); - boolean turnOn = isIn && hasIncomingReferences && visible; - toggleInsButton.setVisible(turnOn); - - boolean isOut = isOutDirection(); - turnOn = isOut && hasOutgoingReferences && visible; - toggleOutsButton.setVisible(turnOn); - } - - @Override - public JComponent getComponent() { - return layeredPane; - } - - @Override - public Shape getCompactShape() { - return compactShape; - } - - @Override - public Shape getFullShape() { - return fullShape; + public void setOutgoingExpanded(boolean setExpanded) { + fcgShapeProvider.setOutgoingExpanded(setExpanded); } @Override @@ -633,6 +245,7 @@ public class FcgVertex extends AbstractVisualVertex implements VertexShapeProvid @Override public void dispose() { - // nothing to do + this.function = null; + super.dispose(); } } diff --git a/Ghidra/Features/GraphFunctionCalls/src/main/java/functioncalls/graph/FcgVertexShapeProvider.java b/Ghidra/Features/GraphFunctionCalls/src/main/java/functioncalls/graph/FcgVertexShapeProvider.java new file mode 100644 index 0000000000..59c0bef93a --- /dev/null +++ b/Ghidra/Features/GraphFunctionCalls/src/main/java/functioncalls/graph/FcgVertexShapeProvider.java @@ -0,0 +1,338 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package functioncalls.graph; + +import java.awt.*; +import java.util.Objects; + +import javax.swing.Icon; + +import functioncalls.plugin.FcgOptions; +import functioncalls.plugin.FunctionCallGraphPlugin; +import generic.theme.GColor; +import generic.theme.Gui; +import ghidra.base.graph.CircleWithLabelVertexShapeProvider; +import ghidra.util.StringUtilities; +import resources.Icons; + +/** + * A vertex shape provider for the {@link FunctionCallGraphPlugin}. + */ +public class FcgVertexShapeProvider extends CircleWithLabelVertexShapeProvider { + + //@formatter:off + private static final Color TOO_BIG_VERTEX_SHAPE_COLOR = new GColor("color.bg.plugin.fcg.vertex.toobig"); + //@formatter:on + + public static final Icon NOT_ALLOWED_ICON = Icons.ERROR_ICON; + + private FcgVertex vertex; + + private Paint inPaint; + private Paint outPaint; + + // these values are set after construction from external sources + private boolean hasIncomingReferences; + private boolean hasOutgoingReferences; + private boolean tooManyIncomingReferences; + private boolean tooManyOutgoingReferences; + + public FcgVertexShapeProvider(FcgVertex vertex, + FcgVertexExpansionListener expansionListener) { + super(vertex.getName()); + this.vertex = vertex; + + Objects.requireNonNull(expansionListener); + + toggleInsButton.addActionListener(e -> { + if (tooManyIncomingReferences) { + return; + } + expansionListener.toggleIncomingVertices(vertex); + }); + + toggleOutsButton.addActionListener(e -> { + if (tooManyOutgoingReferences) { + return; + } + expansionListener.toggleOutgoingVertices(vertex); + }); + + buildUi(); + setTogglesVisible(false); + } + + private void createPaints() { + + Color vertexShapeColor = getDefaultVertexShapeColor(); + + Color lightColor = vertexShapeColor; + Color darkColor = Gui.darker(vertexShapeColor); + Color darkestColor = Gui.darker(darkColor); + FcgLevel level = vertex.getLevel(); + int offset = 5 * level.getDistance(); + int half = VERTEX_SHAPE_SIZE / 2; + int start = 0; + int end = half + offset; + + // paint top-down: dark to light for incoming; light to dark for outgoing + inPaint = new LinearGradientPaint(new Point(0, start), new Point(0, end), + new float[] { .0f, .2f, 1f }, new Color[] { darkestColor, darkColor, lightColor }); + + start = half - offset; // (offset + 10); + end = VERTEX_SHAPE_SIZE; + outPaint = new LinearGradientPaint(new Point(0, start), new Point(0, end), + new float[] { .0f, .8f, 1f }, new Color[] { lightColor, darkColor, darkestColor }); + } + + @Override + protected void buildUi() { + if (vertex == null) { + return; // still being constructed + } + createPaints(); + super.buildUi(); + } + + @Override + protected String generateLabelText() { + FcgOptions options = vertex.getOptions(); + boolean optionsUseTruncatedName = options.useTruncatedFunctionNames(); + String name = getName(); + if (optionsUseTruncatedName) { + name = StringUtilities.trimMiddle(getName(), MAX_NAME_LENGTH); + } + return name; + } + + @Override + protected Paint getVertexShapePaint() { + FcgLevel level = vertex.getLevel(); + FcgDirection direction = level.getDirection(); + if (direction.isSource()) { + return getDefaultVertexShapeColor(); + } + else if (direction.isIn()) { + return inPaint; + } + else { + return outPaint; + } + } + + @Override + protected Color getDefaultVertexShapeColor() { + + if (isInDirection() && tooManyIncomingReferences) { + return TOO_BIG_VERTEX_SHAPE_COLOR; + } + + if (isOutDirection() && tooManyOutgoingReferences) { + return TOO_BIG_VERTEX_SHAPE_COLOR; + } + + return DEFAULT_VERTEX_SHAPE_COLOR; + } + + private boolean isInDirection() { + FcgLevel level = vertex.getLevel(); + FcgDirection direction = level.getDirection(); + boolean isIn = direction.isIn() || direction.isSource(); + return isIn; + } + + private boolean isOutDirection() { + FcgLevel level = vertex.getLevel(); + FcgDirection direction = level.getDirection(); + boolean isOut = direction.isOut() || direction.isSource(); + return isOut; + } + + /** + * Sets to true if this vertex is showing all edges in the incoming direction + * @param setExpanded true if this vertex is showing all edges in the incoming direction + */ + @Override + public void setIncomingExpanded(boolean setExpanded) { + validateIncomingExpandedState(setExpanded); + super.setIncomingExpanded(setExpanded); + } + + private void validateOutgoingExpandedState(boolean isExpanding) { + if (isExpanding) { + if (!canExpandOutgoingReferences()) { + throw new IllegalStateException("Vertex cannot be expanded: " + this); + } + return; + } + + // collapsing + if (!isOutgoingExpanded()) { + throw new IllegalStateException("Vertex cannot be collapsed: " + this); + } + } + + private void validateIncomingExpandedState(boolean expanding) { + + if (expanding) { + if (!canExpandIncomingReferences()) { + throw new IllegalStateException("Vertex cannot be expanded: " + this); + } + return; + } + + // collapsing + if (!isIncomingExpanded()) { + throw new IllegalStateException("Vertex cannot be collapsed: " + this); + } + } + + /** + * Sets to true if this vertex is showing all edges in the outgoing direction + * @param setExpanded true if this vertex is showing all edges in the outgoing direction + */ + @Override + public void setOutgoingExpanded(boolean setExpanded) { + validateOutgoingExpandedState(setExpanded); + super.setOutgoingExpanded(setExpanded); + } + + /** + * Returns whether this vertex is fully expanded in its current direction + * @return whether this vertex is fully expanded in its current direction + */ + @Override + public boolean isExpanded() { + FcgLevel level = vertex.getLevel(); + FcgDirection direction = level.getDirection(); + if (direction.isSource()) { + return isIncomingExpanded() && isOutgoingExpanded(); + } + if (direction.isIn()) { + return isIncomingExpanded(); + } + return isOutgoingExpanded(); + } + + /** + * Sets whether this vertex has too many incoming references, where too many is subjectively + * defined by this class. Too many nodes in the display would ruin rendering and general + * usability. + * + * @param tooMany if there are too many references + */ + public void setTooManyIncomingReferences(boolean tooMany) { + this.tooManyIncomingReferences = tooMany; + toggleInsButton.setIcon(NOT_ALLOWED_ICON); + toggleInsButton.setToolTipText("Too many incoming references to show"); + buildUi(); + } + + /** + * Sets whether this vertex has too many outgoing references, where too many is subjectively + * defined by this class. Too many nodes in the display would ruin rendering and general + * usability. + * + * @param tooMany if there are too many references + */ + public void setTooManyOutgoingReferences(boolean tooMany) { + this.tooManyOutgoingReferences = tooMany; + toggleOutsButton.setIcon(NOT_ALLOWED_ICON); + toggleOutsButton.setToolTipText("Too many outgoing references to show"); + buildUi(); + } + + /** + * Returns whether this vertex has too many incoming references, where too many is subjectively + * defined by this class. Too many nodes in the display would ruin rendering and general + * usability. + * + * @return true if there are too many references + */ + public boolean hasTooManyIncomingReferences() { + return tooManyIncomingReferences; + } + + /** + * Returns whether this vertex has too many outgoing references, where too many is subjectively + * defined by this class. Too many nodes in the display would ruin rendering and general + * usability. + * + * @return true if there are too many references + */ + public boolean hasTooManyOutgoingReferences() { + return tooManyOutgoingReferences; + } + + /** + * Returns true if this vertex can expand itself in its current direction, or in either + * direction if this is a source vertex + * + * @return true if this vertex can be expanded + */ + @Override + public boolean canExpand() { + FcgLevel level = vertex.getLevel(); + FcgDirection direction = level.getDirection(); + if (direction.isSource()) { + return canExpandIncomingReferences() || canExpandOutgoingReferences(); + } + + if (direction.isIn()) { + return canExpandIncomingReferences(); + } + + return canExpandOutgoingReferences(); + } + + public boolean canExpandIncomingReferences() { + return hasIncomingReferences && !tooManyIncomingReferences && !incomingExpanded; + } + + public boolean canExpandOutgoingReferences() { + return hasOutgoingReferences && !tooManyOutgoingReferences && !outgoingExpanded; + } + + /** + * Sets whether this vertex has any incoming references + * + * @param hasIncoming true if this vertex has any incoming references + */ + public void setHasIncomingReferences(boolean hasIncoming) { + this.hasIncomingReferences = hasIncoming; + } + + /** + * Sets whether this vertex has any outgoing references + * @param hasOutgoing true if this vertex has any outgoing references + */ + public void setHasOutgoingReferences(boolean hasOutgoing) { + this.hasOutgoingReferences = hasOutgoing; + } + + @Override + protected void setTogglesVisible(boolean visible) { + + boolean isIn = isInDirection(); + boolean turnOn = isIn && hasIncomingReferences && visible; + toggleInsButton.setVisible(turnOn); + + boolean isOut = isOutDirection(); + turnOn = isOut && hasOutgoingReferences && visible; + toggleOutsButton.setVisible(turnOn); + } + +} diff --git a/Ghidra/Features/GraphFunctionCalls/src/main/java/functioncalls/graph/view/FcgComponent.java b/Ghidra/Features/GraphFunctionCalls/src/main/java/functioncalls/graph/view/FcgComponent.java index 8190df3e8a..fe8b1e2996 100644 --- a/Ghidra/Features/GraphFunctionCalls/src/main/java/functioncalls/graph/view/FcgComponent.java +++ b/Ghidra/Features/GraphFunctionCalls/src/main/java/functioncalls/graph/view/FcgComponent.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -21,6 +21,7 @@ import functioncalls.graph.renderer.FcgEdgePaintTransformer; import functioncalls.graph.renderer.FcgVertexPaintTransformer; import functioncalls.plugin.FunctionCallGraphPlugin; import generic.theme.GColor; +import ghidra.base.graph.CircleWithLabelVertexShapeProvider; import ghidra.graph.viewer.*; import ghidra.graph.viewer.edge.VisualEdgeRenderer; import ghidra.graph.viewer.layout.VisualGraphLayout; @@ -33,7 +34,8 @@ import ghidra.graph.viewer.vertex.VisualVertexRenderer; public class FcgComponent extends GraphComponent { private FcgVertexPaintTransformer vertexPaintTransformer = - new FcgVertexPaintTransformer(FcgVertex.DEFAULT_VERTEX_SHAPE_COLOR); + new FcgVertexPaintTransformer( + CircleWithLabelVertexShapeProvider.DEFAULT_VERTEX_SHAPE_COLOR); private FcgEdgePaintTransformer edgePaintTransformer = new FcgEdgePaintTransformer(new GColor("color.bg.plugin.fcg.edge.primary.direct"), diff --git a/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/vertex/AbstractVisualVertex.java b/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/vertex/AbstractVisualVertex.java index b25deb5623..c57951e0d8 100644 --- a/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/vertex/AbstractVisualVertex.java +++ b/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/vertex/AbstractVisualVertex.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -37,6 +37,7 @@ public abstract class AbstractVisualVertex implements VisualVertex { this.focused = focused; } + @Override public boolean isFocused() { return focused; }