mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 17:59:46 +02:00
Merge remote-tracking branch 'origin/GP-5717-dragonmacher-vertex-shapes'
This commit is contained in:
commit
88f77e5cd1
9 changed files with 882 additions and 466 deletions
|
@ -26,6 +26,8 @@ color.fg.dialog.equates.suggestion = color.palette.hint
|
||||||
color.fg.consoletextpane = color.fg
|
color.fg.consoletextpane = color.fg
|
||||||
color.fg.consoletextpane.error = color.fg.error
|
color.fg.consoletextpane.error = color.fg.error
|
||||||
|
|
||||||
|
color.bg.graph.vertex.function = MediumAquamarine
|
||||||
|
|
||||||
color.fg.infopanel.version = color.fg
|
color.fg.infopanel.version = color.fg
|
||||||
|
|
||||||
color.bg.interpreterconsole = color.bg
|
color.bg.interpreterconsole = color.bg
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -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) + ')';
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -1,9 +1,8 @@
|
||||||
[Defaults]
|
[Defaults]
|
||||||
|
|
||||||
color.bg.plugin.fcg.vertex.default = MediumAquamarine // TODO
|
|
||||||
color.bg.plugin.fcg.vertex.toobig = color.palette.lightgray
|
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.direct.selected = color.palette.lightgreen
|
||||||
color.bg.plugin.fcg.edge.primary.indirect = color.palette.lavender
|
color.bg.plugin.fcg.edge.primary.indirect = color.palette.lavender
|
||||||
color.bg.plugin.fcg.edge.primary.indirect.selected = color.palette.silver
|
color.bg.plugin.fcg.edge.primary.indirect.selected = color.palette.silver
|
||||||
|
|
|
@ -15,88 +15,26 @@
|
||||||
*/
|
*/
|
||||||
package functioncalls.graph;
|
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 java.util.Objects;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.JButton;
|
||||||
import javax.swing.border.Border;
|
|
||||||
import javax.swing.border.LineBorder;
|
|
||||||
|
|
||||||
import docking.widgets.EmptyBorderButton;
|
|
||||||
import docking.widgets.label.GDLabel;
|
|
||||||
import functioncalls.plugin.FcgOptions;
|
import functioncalls.plugin.FcgOptions;
|
||||||
import generic.theme.GColor;
|
import ghidra.base.graph.CircleWithLabelVertex;
|
||||||
import generic.theme.GThemeDefaults.Colors.Palette;
|
|
||||||
import generic.theme.Gui;
|
|
||||||
import ghidra.graph.viewer.vertex.AbstractVisualVertex;
|
|
||||||
import ghidra.graph.viewer.vertex.VertexShapeProvider;
|
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
import ghidra.program.model.listing.Function;
|
import ghidra.program.model.listing.Function;
|
||||||
import ghidra.util.StringUtilities;
|
|
||||||
import resources.Icons;
|
|
||||||
import resources.ResourceManager;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link FunctionCallGraph} vertex
|
* A {@link FunctionCallGraph} vertex
|
||||||
*/
|
*/
|
||||||
public class FcgVertex extends AbstractVisualVertex implements VertexShapeProvider {
|
public class FcgVertex extends CircleWithLabelVertex {
|
||||||
|
|
||||||
//@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;
|
|
||||||
|
|
||||||
private Function function;
|
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 FcgLevel level;
|
||||||
private FcgOptions options;
|
private FcgOptions options;
|
||||||
|
|
||||||
|
private FcgVertexShapeProvider fcgShapeProvider;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
*
|
*
|
||||||
|
@ -107,252 +45,14 @@ public class FcgVertex extends AbstractVisualVertex implements VertexShapeProvid
|
||||||
*/
|
*/
|
||||||
public FcgVertex(Function function, FcgLevel level,
|
public FcgVertex(Function function, FcgLevel level,
|
||||||
FcgVertexExpansionListener expansionListener, FcgOptions options) {
|
FcgVertexExpansionListener expansionListener, FcgOptions options) {
|
||||||
|
super(function.getName());
|
||||||
this.function = function;
|
this.function = function;
|
||||||
this.level = level;
|
this.level = level;
|
||||||
this.options = options;
|
this.options = options;
|
||||||
Objects.requireNonNull(expansionListener);
|
|
||||||
|
|
||||||
toggleInsButton.addActionListener(e -> {
|
fcgShapeProvider = new FcgVertexShapeProvider(this, expansionListener);
|
||||||
if (tooManyIncomingReferences) {
|
shapeProvider = fcgShapeProvider;
|
||||||
return;
|
|
||||||
}
|
|
||||||
expansionListener.toggleIncomingVertices(FcgVertex.this);
|
|
||||||
});
|
|
||||||
|
|
||||||
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() {
|
public Function getFunction() {
|
||||||
|
@ -363,6 +63,10 @@ public class FcgVertex extends AbstractVisualVertex implements VertexShapeProvid
|
||||||
return function.getEntryPoint();
|
return function.getEntryPoint();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public FcgOptions getOptions() {
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
public FcgLevel getLevel() {
|
public FcgLevel getLevel() {
|
||||||
return level;
|
return level;
|
||||||
}
|
}
|
||||||
|
@ -375,105 +79,35 @@ public class FcgVertex extends AbstractVisualVertex implements VertexShapeProvid
|
||||||
return level.getDirection();
|
return level.getDirection();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setHovered(boolean hovered) {
|
||||||
|
super.setHovered(hovered);
|
||||||
|
fcgShapeProvider.setTogglesVisible(hovered);
|
||||||
|
}
|
||||||
|
|
||||||
public JButton getIncomingToggleButton() {
|
public JButton getIncomingToggleButton() {
|
||||||
return toggleInsButton;
|
return shapeProvider.getIncomingToggleButton();
|
||||||
}
|
}
|
||||||
|
|
||||||
public JButton getOutgoingToggleButton() {
|
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) {
|
public void setHasIncomingReferences(boolean hasIncoming) {
|
||||||
|
fcgShapeProvider.setHasIncomingReferences(hasIncoming);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns 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
|
||||||
* @return true if this vertex is showing all edges in the incoming direction
|
|
||||||
*/
|
*/
|
||||||
public boolean isIncomingExpanded() {
|
public void setHasOutgoingReferences(boolean hasOutgoing) {
|
||||||
return incomingExpanded;
|
fcgShapeProvider.setHasOutgoingReferences(hasOutgoing);
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -484,10 +118,7 @@ public class FcgVertex extends AbstractVisualVertex implements VertexShapeProvid
|
||||||
* @param tooMany if there are too many references
|
* @param tooMany if there are too many references
|
||||||
*/
|
*/
|
||||||
public void setTooManyIncomingReferences(boolean tooMany) {
|
public void setTooManyIncomingReferences(boolean tooMany) {
|
||||||
this.tooManyIncomingReferences = tooMany;
|
fcgShapeProvider.setTooManyIncomingReferences(tooMany);
|
||||||
toggleInsButton.setIcon(NOT_ALLOWED_ICON);
|
|
||||||
toggleInsButton.setToolTipText("Too many incoming references to show");
|
|
||||||
buildUi();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -498,10 +129,7 @@ public class FcgVertex extends AbstractVisualVertex implements VertexShapeProvid
|
||||||
* @param tooMany if there are too many references
|
* @param tooMany if there are too many references
|
||||||
*/
|
*/
|
||||||
public void setTooManyOutgoingReferences(boolean tooMany) {
|
public void setTooManyOutgoingReferences(boolean tooMany) {
|
||||||
this.tooManyOutgoingReferences = tooMany;
|
fcgShapeProvider.setTooManyOutgoingReferences(tooMany);
|
||||||
toggleOutsButton.setIcon(NOT_ALLOWED_ICON);
|
|
||||||
toggleOutsButton.setToolTipText("Too many outgoing references to show");
|
|
||||||
buildUi();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -512,7 +140,7 @@ public class FcgVertex extends AbstractVisualVertex implements VertexShapeProvid
|
||||||
* @return true if there are too many references
|
* @return true if there are too many references
|
||||||
*/
|
*/
|
||||||
public boolean hasTooManyIncomingReferences() {
|
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
|
* @return true if there are too many references
|
||||||
*/
|
*/
|
||||||
public boolean hasTooManyOutgoingReferences() {
|
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
|
* @return true if this vertex can be expanded
|
||||||
*/
|
*/
|
||||||
public boolean canExpand() {
|
public boolean canExpand() {
|
||||||
FcgDirection direction = level.getDirection();
|
return fcgShapeProvider.canExpand();
|
||||||
if (direction.isSource()) {
|
|
||||||
return canExpandIncomingReferences() || canExpandOutgoingReferences();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (direction.isIn()) {
|
|
||||||
return canExpandIncomingReferences();
|
|
||||||
}
|
|
||||||
|
|
||||||
return canExpandOutgoingReferences();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean canExpandIncomingReferences() {
|
public boolean canExpandIncomingReferences() {
|
||||||
return hasIncomingReferences && !tooManyIncomingReferences && !incomingExpanded;
|
return fcgShapeProvider.canExpandIncomingReferences();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean canExpandOutgoingReferences() {
|
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) {
|
public void setIncomingExpanded(boolean setExpanded) {
|
||||||
this.hasIncomingReferences = hasIncoming;
|
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 setOutgoingExpanded(boolean setExpanded) {
|
||||||
public void setHasOutgoingReferences(boolean hasOutgoing) {
|
fcgShapeProvider.setOutgoingExpanded(setExpanded);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -633,6 +245,7 @@ public class FcgVertex extends AbstractVisualVertex implements VertexShapeProvid
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void dispose() {
|
public void dispose() {
|
||||||
// nothing to do
|
this.function = null;
|
||||||
|
super.dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -21,6 +21,7 @@ import functioncalls.graph.renderer.FcgEdgePaintTransformer;
|
||||||
import functioncalls.graph.renderer.FcgVertexPaintTransformer;
|
import functioncalls.graph.renderer.FcgVertexPaintTransformer;
|
||||||
import functioncalls.plugin.FunctionCallGraphPlugin;
|
import functioncalls.plugin.FunctionCallGraphPlugin;
|
||||||
import generic.theme.GColor;
|
import generic.theme.GColor;
|
||||||
|
import ghidra.base.graph.CircleWithLabelVertexShapeProvider;
|
||||||
import ghidra.graph.viewer.*;
|
import ghidra.graph.viewer.*;
|
||||||
import ghidra.graph.viewer.edge.VisualEdgeRenderer;
|
import ghidra.graph.viewer.edge.VisualEdgeRenderer;
|
||||||
import ghidra.graph.viewer.layout.VisualGraphLayout;
|
import ghidra.graph.viewer.layout.VisualGraphLayout;
|
||||||
|
@ -33,7 +34,8 @@ import ghidra.graph.viewer.vertex.VisualVertexRenderer;
|
||||||
public class FcgComponent extends GraphComponent<FcgVertex, FcgEdge, FunctionCallGraph> {
|
public class FcgComponent extends GraphComponent<FcgVertex, FcgEdge, FunctionCallGraph> {
|
||||||
|
|
||||||
private FcgVertexPaintTransformer vertexPaintTransformer =
|
private FcgVertexPaintTransformer vertexPaintTransformer =
|
||||||
new FcgVertexPaintTransformer(FcgVertex.DEFAULT_VERTEX_SHAPE_COLOR);
|
new FcgVertexPaintTransformer(
|
||||||
|
CircleWithLabelVertexShapeProvider.DEFAULT_VERTEX_SHAPE_COLOR);
|
||||||
|
|
||||||
private FcgEdgePaintTransformer edgePaintTransformer =
|
private FcgEdgePaintTransformer edgePaintTransformer =
|
||||||
new FcgEdgePaintTransformer(new GColor("color.bg.plugin.fcg.edge.primary.direct"),
|
new FcgEdgePaintTransformer(new GColor("color.bg.plugin.fcg.edge.primary.direct"),
|
||||||
|
|
|
@ -37,6 +37,7 @@ public abstract class AbstractVisualVertex implements VisualVertex {
|
||||||
this.focused = focused;
|
this.focused = focused;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean isFocused() {
|
public boolean isFocused() {
|
||||||
return focused;
|
return focused;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue