mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 10:19:23 +02:00
GT-3019 - Function Graph - Edge Routing
-Added layout-specific help -Added option for controlling the size of tooltips -Fixed edge label placement bug in the Visual Graph system
This commit is contained in:
parent
a04185e942
commit
17b8e54f25
16 changed files with 289 additions and 63 deletions
|
@ -19,6 +19,7 @@ src/main/help/help/shared/tip.png||Oxygen Icons - LGPL 3.0|||renamed oxygen file
|
|||
src/main/help/help/shared/undo.png||GHIDRA||reviewed||END|
|
||||
src/main/help/help/shared/warning.png||Oxygen Icons - LGPL 3.0||||END|
|
||||
src/main/help/help/topics/FunctionGraphPlugin/Function_Graph.html||GHIDRA|||contains screenshots of various icons with IP and listing windows|END|
|
||||
src/main/help/help/topics/FunctionGraphPlugin/Function_Graph_Layouts.html||GHIDRA||||END|
|
||||
src/main/help/help/topics/FunctionGraphPlugin/images/FunctionGraphWindow.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/FunctionGraphPlugin/images/FunctionGraph_Action_Layout.png||GHIDRA||reviewed||END|
|
||||
src/main/help/help/topics/FunctionGraphPlugin/images/FunctionGraph_Action_Show_All_Loops_In_Function.png||GHIDRA||reviewed|created in-house|END|
|
||||
|
|
|
@ -63,6 +63,9 @@
|
|||
<tocdef id="Graph Actions" sortgroup="e" text="Graph Actions" target="help/topics/FunctionGraphPlugin/Function_Graph.html#Function_Graph_Actions" />
|
||||
<tocdef id="Popups" sortgroup="f" text="Popups" target="help/topics/FunctionGraphPlugin/Function_Graph.html#Popups" />
|
||||
<tocdef id="Zooming" sortgroup="g" text="Zooming" target="help/topics/FunctionGraphPlugin/Function_Graph.html#Zoom" />
|
||||
|
||||
<tocdef id="Layouts" sortgroup="h" text="Layouts" target="help/topics/FunctionGraphPlugin/Function_Graph_Layouts.html" />
|
||||
|
||||
</tocdef>
|
||||
</tocref>
|
||||
|
||||
|
|
|
@ -809,6 +809,11 @@
|
|||
option to fit as many vertices on the screen as possible. Disable this option to make the
|
||||
overall layout of the graph more aesthetic.</P>
|
||||
|
||||
<P>The <B>Use Full-size Tooltip</B>When toggled off, the tooltip for a vertex will be
|
||||
the same size and layout of the vertices in the graph. When toggled on, the tooltip
|
||||
for a vertex will be larger, using the layout of the Listing. The larger size is
|
||||
more informative, but also takes up more space.</P>
|
||||
|
||||
<P>The <B>Use Mouse-relative Zoom</B> option signals zoom the graph to and from the mouse
|
||||
location when zooming from the middle-mouse. The default for this option is off, which
|
||||
triggers zoom to work from the center of the graph, regardless of the mouse location.</P>
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
<!DOCTYPE doctype PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN">
|
||||
|
||||
<HTML>
|
||||
<HEAD>
|
||||
<META name="generator" content=
|
||||
"HTML Tidy for Java (vers. 2009-12-01), see jtidy.sourceforge.net">
|
||||
|
||||
<TITLE>Function Graph Plugin</TITLE>
|
||||
<META http-equiv="Content-Type" content="text/html; charset=windows-1252">
|
||||
<LINK rel="stylesheet" type="text/css" href="../../shared/Frontpage.css">
|
||||
</HEAD>
|
||||
|
||||
<BODY lang="EN-US">
|
||||
<H1><A name="Function_Graph_Layouts"></A>Function Graph Layouts</H1>
|
||||
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<H2><A name="Nested_Code_Layout"></A>Nested Code Layout</H2>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>The nested code layout use the
|
||||
<A name="help/topics/DecompilePlugin/Decompiler.htm">Decompiler</A> to arrange the
|
||||
code blocks of a function in a way that mimics the nesting of the source code as seen
|
||||
in the decompiled function. As an example, any code block that must pass through an
|
||||
<code>if</code> statement will be nested below and to the right of the code block that
|
||||
contains the conditional check. The nested code block is <B>dominated</B> by the block
|
||||
containing the conditional check--code flow can only reach the nested block by passing
|
||||
through the block above it. Also, code blocks that represent a default code flow
|
||||
will be aligned to the left and below other code blocks in the function. This layout
|
||||
allows the user to quickly see the dominating relationships between code blocks.
|
||||
</P>
|
||||
<P>The edges leaving a code block are labeled with the type of high-level conditional
|
||||
statement (e.g., <code>if</code>, <code>if/else</code>, etc) used to determine code flow.
|
||||
</P>
|
||||
<P>By default, edges are routed such that they are grouped together such that any edges
|
||||
returning to a shared code block will overlap. This reduces visual clutter at the
|
||||
expense of being able to visually follow individual edges to their vertices. Another
|
||||
consequence of this routing is that sometimes edges will travel behind unrelated
|
||||
vertices, again, making it difficult to visually follow these edges. The edge
|
||||
routing can be changed via the options below.
|
||||
</P>
|
||||
|
||||
|
||||
<H3><A name="Nested_Code_Layout_Options"></A>Nested Code Layout Options</H3>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>The <B>Route Edges Around Vertices</B> option triggers this layout to route
|
||||
edges around any vertex that would otherwise touch that edge. (See above for
|
||||
notes on how edges are routed for this layout.)
|
||||
</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
</BLOCKQUOTE>
|
||||
|
||||
|
||||
<P class="providedbyplugin">Provided by: <I>Function Graph Plugin</I></P>
|
||||
<BR>
|
||||
</BODY>
|
||||
</HTML>
|
|
@ -574,6 +574,17 @@ public class FGProvider extends VisualGraphComponentProvider<FGVertex, FGEdge, F
|
|||
refresh(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells the graph that some display data may have changed, but the changes are not worth
|
||||
* performing a full rebuild
|
||||
*/
|
||||
public void refreshDisplayWithoutRebuilding() {
|
||||
FGData functionGraphData = controller.getFunctionGraphData();
|
||||
if (functionGraphData.hasResults()) {
|
||||
controller.refreshDisplayWithoutRebuilding();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void domainObjectChanged(DomainObjectChangedEvent ev) {
|
||||
if (!isVisible()) {
|
||||
|
|
|
@ -169,19 +169,23 @@ public class FunctionGraphPlugin extends ProgramPlugin implements OptionsChangeL
|
|||
@Override
|
||||
public void optionsChanged(ToolOptions options, String optionName, Object oldValue,
|
||||
Object newValue) {
|
||||
functionGraphOptions.loadOptions(options);
|
||||
connectedProvider.getComponent().repaint();
|
||||
for (FGProvider provider : disconnectedProviders) {
|
||||
provider.getComponent().repaint();
|
||||
}
|
||||
|
||||
if (VisualGraphOptions.USE_CONDENSED_LAYOUT.equals(optionName)) {
|
||||
// the condensed setting requires us to reposition the graph
|
||||
functionGraphOptions.loadOptions(options);
|
||||
|
||||
if (functionGraphOptions.optionChangeRequiresRelayout(optionName)) {
|
||||
connectedProvider.refreshAndKeepPerspective();
|
||||
}
|
||||
else if (VisualGraphOptions.VIEW_RESTORE_OPTIONS_KEY.equals(optionName)) {
|
||||
connectedProvider.clearViewSettings();
|
||||
}
|
||||
else {
|
||||
connectedProvider.refreshDisplayWithoutRebuilding();
|
||||
}
|
||||
|
||||
connectedProvider.getComponent().repaint();
|
||||
for (FGProvider provider : disconnectedProviders) {
|
||||
provider.getComponent().repaint();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -24,7 +24,6 @@ import org.jdom.Element;
|
|||
import edu.uci.ics.jung.algorithms.layout.Layout;
|
||||
import edu.uci.ics.jung.visualization.RenderContext;
|
||||
import edu.uci.ics.jung.visualization.picking.PickedState;
|
||||
import edu.uci.ics.jung.visualization.renderers.DefaultEdgeLabelRenderer;
|
||||
import edu.uci.ics.jung.visualization.renderers.Renderer;
|
||||
import edu.uci.ics.jung.visualization.util.Caching;
|
||||
import ghidra.app.plugin.core.functiongraph.graph.jung.renderer.FGEdgePaintTransformer;
|
||||
|
@ -37,6 +36,7 @@ import ghidra.graph.viewer.*;
|
|||
import ghidra.graph.viewer.layout.LayoutListener.ChangeType;
|
||||
import ghidra.graph.viewer.layout.LayoutProvider;
|
||||
import ghidra.graph.viewer.layout.VisualGraphLayout;
|
||||
import ghidra.graph.viewer.renderer.VisualGraphEdgeLabelRenderer;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.util.SystemUtilities;
|
||||
|
@ -208,7 +208,12 @@ public class FGComponent extends GraphComponent<FGVertex, FGEdge, FunctionGraph>
|
|||
// edge label rendering
|
||||
com.google.common.base.Function<FGEdge, String> edgeLabelTransformer = e -> e.getLabel();
|
||||
renderContext.setEdgeLabelTransformer(edgeLabelTransformer);
|
||||
DefaultEdgeLabelRenderer edgeLabelRenderer = new DefaultEdgeLabelRenderer(Color.ORANGE);
|
||||
|
||||
// note: this label renderer is the stamp for the label; we use another edge label
|
||||
// renderer inside of the VisualGraphRenderer
|
||||
VisualGraphEdgeLabelRenderer edgeLabelRenderer =
|
||||
new VisualGraphEdgeLabelRenderer(Color.BLACK);
|
||||
edgeLabelRenderer.setNonPickedForegroundColor(Color.LIGHT_GRAY);
|
||||
edgeLabelRenderer.setRotateEdgeLabels(false);
|
||||
renderContext.setEdgeLabelRenderer(edgeLabelRenderer);
|
||||
|
||||
|
|
|
@ -40,4 +40,13 @@ public interface FGLayoutOptions {
|
|||
* @param options the tool options
|
||||
*/
|
||||
public void loadOptions(Options options);
|
||||
|
||||
/**
|
||||
* Returns true if the given option name, when changed, requires that the current graph be
|
||||
* reloaded for the change to take effect
|
||||
*
|
||||
* @param optionName the changed option name
|
||||
* @return true if a relayout is required
|
||||
*/
|
||||
public boolean optionChangeRequiresRelayout(String optionName);
|
||||
}
|
||||
|
|
|
@ -198,12 +198,23 @@ public class ListingGraphComponentPanel extends AbstractGraphComponentPanel {
|
|||
|
||||
private void createListingPanelToolTipComponent() {
|
||||
JPanel panel = new JPanel(new BorderLayout());
|
||||
previewListingPanel =
|
||||
new FGVertexListingPanel(controller, getFormatManager(true), program, addressSet);
|
||||
|
||||
FunctionGraphOptions options = controller.getFunctionGraphOptions();
|
||||
boolean useFullSizeTooltip = options.useFullSizeTooltip();
|
||||
previewListingPanel = new FGVertexListingPanel(controller,
|
||||
getFormatManager(useFullSizeTooltip), program, addressSet);
|
||||
previewListingPanel.setTextBackgroundColor(FGVertex.TOOLTIP_BACKGROUND_COLOR);
|
||||
// previewListingPanel.getFieldPanel().setSelectionMode( FieldPanel.NO_SELECTION );
|
||||
previewListingPanel.getFieldPanel().setCursorOn(false);
|
||||
|
||||
// keep the tooltip window from getting too big; use an arbitrary, reasonable max
|
||||
Dimension maxSize = new Dimension(700, 400);
|
||||
previewListingPanel.setMaximumSize(maxSize);
|
||||
Dimension preferredSize = previewListingPanel.getPreferredSize();
|
||||
preferredSize.width = Math.min(maxSize.width, preferredSize.width);
|
||||
preferredSize.height = Math.min(maxSize.height, preferredSize.height);
|
||||
previewListingPanel.setPreferredSize(preferredSize);
|
||||
|
||||
tooltipTitleLabel = new GDLabel();
|
||||
tooltipTitleLabel.setHorizontalAlignment(SwingConstants.LEADING);
|
||||
tooltipTitleLabel.setBackground(FGVertex.TOOLTIP_BACKGROUND_COLOR);
|
||||
|
@ -239,6 +250,7 @@ public class ListingGraphComponentPanel extends AbstractGraphComponentPanel {
|
|||
// make sure the title stays up-to-date with the symbol at the start address
|
||||
title = createTitle();
|
||||
genericHeader.setTitle(title);
|
||||
previewListingPanel = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -41,6 +41,12 @@ public class FunctionGraphOptions extends VisualGraphOptions {
|
|||
"Edge Color - Unconditional Jump ";
|
||||
private static final String EDGE_COLOR_CONDITIONAL_JUMP_KEY = "Edge Color - Conditional Jump ";
|
||||
|
||||
private static final String USE_FULL_SIZE_TOOLTIP_KEY = "Use Full-size Tooltip";
|
||||
private static final String USE_FULL_SIZE_TOOLTIP_DESCRIPTION = "Signals to use the " + "" +
|
||||
"full-size vertex inside of the tooltip popup. When enabled the tooltip vertex will " +
|
||||
"use the same format size as the Listing. When disabled, the vertex will use the " +
|
||||
"same format size as in the Function Graph.";
|
||||
|
||||
public static final String RELAYOUT_OPTIONS_KEY = "Automatic Graph Relayout";
|
||||
public static final String RELAYOUT_OPTIONS_DESCRIPTION = "Signals to the Function Graph " +
|
||||
"when an automatic relayout of the graph should take place. The basic options are:<ul>" +
|
||||
|
@ -48,7 +54,7 @@ public class FunctionGraphOptions extends VisualGraphOptions {
|
|||
"<li><b>Block Model Changes Only</b> - relayout the graph when the block model changes " +
|
||||
"(like when a label has been added to the program in the currently graphed function)</li>" +
|
||||
"<li><b>Vertex Grouping Changes Only</b> - when vertices are grouped or ungrouped</li>" +
|
||||
"<li><b>Never</b> - do not automatically relayout the graph</li></ul>" + "<br><br>" +
|
||||
"<li><b>Never</b> - do not automatically relayout the graph</li></ul><br><br>" +
|
||||
"<b><i>See help for more</i></b>";
|
||||
|
||||
private static final String DEFAULT_GROUP_BACKGROUND_COLOR_KEY = "Default Group Color";
|
||||
|
@ -77,7 +83,9 @@ public class FunctionGraphOptions extends VisualGraphOptions {
|
|||
private Color unconditionalJumpEdgeHighlightColor = HOVER_HIGHLIGHT_UNCONDITIONAL_COLOR;
|
||||
private Color conditionalJumpEdgeHighlightColor = HOVER_HIGHLIGHT_CONDITIONAL_COLOR;
|
||||
|
||||
protected RelayoutOption relayoutOption = RelayoutOption.NEVER;
|
||||
private boolean useFullSizeTooltip = false;
|
||||
|
||||
private RelayoutOption relayoutOption = RelayoutOption.NEVER;
|
||||
|
||||
private Map<String, FGLayoutOptions> layoutOptionsByName = new HashMap<>();
|
||||
|
||||
|
@ -117,6 +125,10 @@ public class FunctionGraphOptions extends VisualGraphOptions {
|
|||
return relayoutOption;
|
||||
}
|
||||
|
||||
public boolean useFullSizeTooltip() {
|
||||
return useFullSizeTooltip;
|
||||
}
|
||||
|
||||
public void registerOptions(Options options) {
|
||||
|
||||
HelpLocation help = new HelpLocation(OWNER, "Options");
|
||||
|
@ -128,10 +140,10 @@ public class FunctionGraphOptions extends VisualGraphOptions {
|
|||
options.registerOption(SHOW_ANIMATION_OPTIONS_KEY, useAnimation(), help,
|
||||
SHOW_ANIMATION_DESCRIPTION);
|
||||
|
||||
options.registerOption(USE_MOUSE_RELATIVE_ZOOM, useMouseRelativeZoom(), help,
|
||||
options.registerOption(USE_MOUSE_RELATIVE_ZOOM_KEY, useMouseRelativeZoom(), help,
|
||||
USE_MOUSE_RELATIVE_ZOOM_DESCRIPTION);
|
||||
|
||||
options.registerOption(USE_CONDENSED_LAYOUT, useCondensedLayout(),
|
||||
options.registerOption(USE_CONDENSED_LAYOUT_KEY, useCondensedLayout(),
|
||||
new HelpLocation(OWNER, "Layout_Compressing"), USE_CONDENSED_LAYOUT_DESCRIPTION);
|
||||
|
||||
options.registerOption(VIEW_RESTORE_OPTIONS_KEY, ViewRestoreOption.START_FULLY_ZOOMED_OUT,
|
||||
|
@ -146,6 +158,9 @@ public class FunctionGraphOptions extends VisualGraphOptions {
|
|||
options.registerOption(UPDATE_GROUP_AND_UNGROUP_COLORS, updateGroupColorsAutomatically,
|
||||
help, UPDATE_GROUP_AND_UNGROUP_COLORS_DESCRIPTION);
|
||||
|
||||
options.registerOption(USE_FULL_SIZE_TOOLTIP_KEY, useFullSizeTooltip, help,
|
||||
USE_FULL_SIZE_TOOLTIP_DESCRIPTION);
|
||||
|
||||
options.registerOption(EDGE_COLOR_CONDITIONAL_JUMP_KEY, conditionalJumpEdgeColor, help,
|
||||
"Conditional jump edge color");
|
||||
|
||||
|
@ -191,9 +206,12 @@ public class FunctionGraphOptions extends VisualGraphOptions {
|
|||
|
||||
useAnimation = options.getBoolean(SHOW_ANIMATION_OPTIONS_KEY, useAnimation);
|
||||
|
||||
useMouseRelativeZoom = options.getBoolean(USE_MOUSE_RELATIVE_ZOOM, useMouseRelativeZoom);
|
||||
useMouseRelativeZoom =
|
||||
options.getBoolean(USE_MOUSE_RELATIVE_ZOOM_KEY, useMouseRelativeZoom);
|
||||
|
||||
useCondensedLayout = options.getBoolean(USE_CONDENSED_LAYOUT, useCondensedLayout);
|
||||
useCondensedLayout = options.getBoolean(USE_CONDENSED_LAYOUT_KEY, useCondensedLayout);
|
||||
|
||||
useFullSizeTooltip = options.getBoolean(USE_FULL_SIZE_TOOLTIP_KEY, useFullSizeTooltip);
|
||||
|
||||
viewRestoreOption =
|
||||
options.getEnum(VIEW_RESTORE_OPTIONS_KEY, ViewRestoreOption.START_FULLY_ZOOMED_OUT);
|
||||
|
@ -243,6 +261,22 @@ public class FunctionGraphOptions extends VisualGraphOptions {
|
|||
return Color.BLACK;
|
||||
}
|
||||
|
||||
public boolean optionChangeRequiresRelayout(String optionName) {
|
||||
if (USE_CONDENSED_LAYOUT_KEY.equals(optionName)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Set<Entry<String, FGLayoutOptions>> entries = layoutOptionsByName.entrySet();
|
||||
for (Entry<String, FGLayoutOptions> entry : entries) {
|
||||
FGLayoutOptions layoutOptions = entry.getValue();
|
||||
if (layoutOptions.optionChangeRequiresRelayout(optionName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public FGLayoutOptions getLayoutOptions(String layoutName) {
|
||||
return layoutOptionsByName.get(layoutName);
|
||||
}
|
||||
|
@ -250,4 +284,5 @@ public class FunctionGraphOptions extends VisualGraphOptions {
|
|||
public void setLayoutOptions(String layoutName, FGLayoutOptions options) {
|
||||
layoutOptionsByName.put(layoutName, options);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -20,6 +20,8 @@ import java.awt.geom.AffineTransform;
|
|||
import java.awt.geom.Point2D;
|
||||
import java.util.List;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
|
||||
import edu.uci.ics.jung.algorithms.layout.Layout;
|
||||
import edu.uci.ics.jung.graph.Graph;
|
||||
import edu.uci.ics.jung.graph.util.Context;
|
||||
|
@ -32,52 +34,63 @@ import ghidra.app.plugin.core.functiongraph.graph.FGEdge;
|
|||
import ghidra.app.plugin.core.functiongraph.graph.vertex.FGVertex;
|
||||
import ghidra.graph.viewer.vertex.VisualGraphVertexShapeTransformer;
|
||||
|
||||
class CodeFlowEdgeLabelRenderer<V extends FGVertex, E extends FGEdge>
|
||||
/**
|
||||
* An edge label renderer used with the {@link DecompilerNestedLayout}
|
||||
*
|
||||
* @param <V> the vertex type
|
||||
* @param <E> the edge type
|
||||
*/
|
||||
class DNLEdgeLabelRenderer<V extends FGVertex, E extends FGEdge>
|
||||
implements Renderer.EdgeLabel<V, E> {
|
||||
|
||||
private static final int EDGE_OFFSET = 20;
|
||||
private static final int DEFAULT_EDGE_OFFSET = 20;
|
||||
|
||||
VisualGraphVertexShapeTransformer vertexShapeTransformer =
|
||||
new VisualGraphVertexShapeTransformer();
|
||||
private VisualGraphVertexShapeTransformer<V> vertexShapeTransformer =
|
||||
new VisualGraphVertexShapeTransformer<>();
|
||||
|
||||
private double edgeOffset;
|
||||
|
||||
DNLEdgeLabelRenderer(double condenseFactor) {
|
||||
this.edgeOffset = DEFAULT_EDGE_OFFSET * (1 - condenseFactor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void labelEdge(RenderContext<V, E> rc, Layout<V, E> layout, E e, String text) {
|
||||
|
||||
if (text == null || text.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Graph<V, E> jungGraph = layout.getGraph();
|
||||
if (!rc.getEdgeIncludePredicate().apply(Context.getInstance(jungGraph, e))) {
|
||||
return;
|
||||
}
|
||||
|
||||
Pair<V> endpoints = jungGraph.getEndpoints(e);
|
||||
V v1 = endpoints.getFirst();
|
||||
V v2 = endpoints.getSecond();
|
||||
if (!rc.getEdgeIncludePredicate().apply(
|
||||
Context.<Graph<V, E>, E> getInstance(jungGraph, e))) {
|
||||
V startv = endpoints.getFirst();
|
||||
V endv = endpoints.getSecond();
|
||||
|
||||
Predicate<Context<Graph<V, E>, V>> includeVertex = rc.getVertexIncludePredicate();
|
||||
if (!includeVertex.apply(Context.getInstance(jungGraph, startv)) ||
|
||||
!includeVertex.apply(Context.getInstance(jungGraph, endv))) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!rc.getVertexIncludePredicate().apply(
|
||||
Context.<Graph<V, E>, V> getInstance(jungGraph, v1)) ||
|
||||
!rc.getVertexIncludePredicate().apply(
|
||||
Context.<Graph<V, E>, V> getInstance(jungGraph, v2))) {
|
||||
return;
|
||||
}
|
||||
|
||||
Point2D p1 = layout.apply(v1);
|
||||
Point2D start = layout.apply(startv);
|
||||
MultiLayerTransformer multiLayerTransformer = rc.getMultiLayerTransformer();
|
||||
p1 = multiLayerTransformer.transform(Layer.LAYOUT, p1);
|
||||
start = multiLayerTransformer.transform(Layer.LAYOUT, start);
|
||||
|
||||
Shape vertexShape = vertexShapeTransformer.apply(v1);
|
||||
Shape vertexShape = vertexShapeTransformer.apply(startv);
|
||||
Rectangle vertexBounds = vertexShape.getBounds();
|
||||
int xDisplacement = rc.getLabelOffset();
|
||||
|
||||
Point2D labelPointOffset = new Point2D.Double();
|
||||
|
||||
// note: location is centered
|
||||
double cx = start.getX();
|
||||
double cy = start.getY();
|
||||
|
||||
List<Point2D> articulationPoints = e.getArticulationPoints();
|
||||
if (articulationPoints.isEmpty()) {
|
||||
double vertexBottom = p1.getY() + (vertexBounds.height >> 1); // location is centered
|
||||
int textY = (int) (vertexBottom + EDGE_OFFSET); // below the vertex; above the bend
|
||||
int textX = (int) (p1.getX() + xDisplacement); // right of the edge
|
||||
double vertexBottom = start.getY() + (vertexBounds.height >> 1); // location is centered
|
||||
int textY = (int) (vertexBottom + edgeOffset); // below the vertex; above the bend
|
||||
int textX = (int) (start.getX() + xDisplacement); // right of the edge
|
||||
labelPointOffset.setLocation(textX, textY);
|
||||
}
|
||||
else if (articulationPoints.size() == 1) {
|
||||
|
@ -91,24 +104,25 @@ class CodeFlowEdgeLabelRenderer<V extends FGVertex, E extends FGEdge>
|
|||
Point2D bend2 = articulationPoints.get(1);
|
||||
bend2 = multiLayerTransformer.transform(Layer.LAYOUT, bend2);
|
||||
|
||||
double vertexSide = cx + (vertexBounds.width >> 1);
|
||||
double vertexBottom = cy + (vertexBounds.height >> 1);
|
||||
|
||||
double bx1 = bend1.getX();
|
||||
|
||||
if (articulationPoints.size() == 2) {
|
||||
|
||||
double vertexSide = p1.getX() + (vertexBounds.width >> 1); // location is centered
|
||||
int textX = (int) (vertexSide + EDGE_OFFSET); // right of the vertex
|
||||
int textY = (int) (p1.getY() + EDGE_OFFSET); // above the edge
|
||||
int textX = (int) (vertexSide + edgeOffset); // right of the vertex
|
||||
int textY = (int) (cy + edgeOffset); // above the edge
|
||||
labelPointOffset.setLocation(textX, textY);
|
||||
}
|
||||
else if (articulationPoints.size() == 3) {
|
||||
double vertexBottom = p1.getY() + (vertexBounds.height >> 1); // location is centered
|
||||
int textY = (int) (vertexBottom + EDGE_OFFSET); // below the vertex; above the bend
|
||||
|
||||
int textY = (int) (vertexBottom + edgeOffset); // below the vertex; above the bend
|
||||
int textX = (int) (bx1 + xDisplacement); // right of the edge
|
||||
labelPointOffset.setLocation(textX, textY);
|
||||
}
|
||||
else if (articulationPoints.size() == 4) {
|
||||
double vertexBottom = p1.getY() + (vertexBounds.height >> 1); // location is centered
|
||||
int textY = (int) (vertexBottom + EDGE_OFFSET); // below the vertex; above the bend
|
||||
else { // if (articulationPoints.size() == 4) {
|
||||
int textY = (int) (vertexBottom + edgeOffset); // below the vertex; above the bend
|
||||
int textX = (int) (bx1 + xDisplacement); // right of the edge
|
||||
labelPointOffset.setLocation(textX, textY);
|
||||
}
|
|
@ -23,6 +23,8 @@ import ghidra.util.HelpLocation;
|
|||
*/
|
||||
public class DNLayoutOptions implements FGLayoutOptions {
|
||||
|
||||
private static final String HELP_ANCHOR =
|
||||
DecompilerNestedLayoutProvider.LAYOUT_NAME + "_Options";
|
||||
private static final String USE_EDGE_ROUTING_AROUND_VERTICES_KEY =
|
||||
"Route Edges Around Vertices";
|
||||
private static final String USE_EDGE_ROUTING_AROUND_VERTICES_DESCRIPTION = "Signals that " +
|
||||
|
@ -34,8 +36,7 @@ public class DNLayoutOptions implements FGLayoutOptions {
|
|||
@Override
|
||||
public void registerOptions(Options options) {
|
||||
|
||||
// TODO layout-specific help
|
||||
HelpLocation help = new HelpLocation(OWNER, "Options");
|
||||
HelpLocation help = new HelpLocation(OWNER, HELP_ANCHOR);
|
||||
|
||||
options.registerOption(USE_EDGE_ROUTING_AROUND_VERTICES_KEY, useEdgeRoutingAroundVertices,
|
||||
help, USE_EDGE_ROUTING_AROUND_VERTICES_DESCRIPTION);
|
||||
|
@ -50,4 +51,10 @@ public class DNLayoutOptions implements FGLayoutOptions {
|
|||
public boolean useEdgeRoutingAroundVertices() {
|
||||
return useEdgeRoutingAroundVertices;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean optionChangeRequiresRelayout(String optionName) {
|
||||
// format: 'Nested Code Layout.Route Edges....'
|
||||
return optionName.endsWith(USE_EDGE_ROUTING_AROUND_VERTICES_KEY);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -102,7 +102,7 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
|
|||
|
||||
@Override
|
||||
public EdgeLabel<FGVertex, FGEdge> getEdgeLabelRenderer() {
|
||||
return new CodeFlowEdgeLabelRenderer<>();
|
||||
return new DNLEdgeLabelRenderer<>(getCondenseFactor());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -23,11 +23,11 @@ public class VisualGraphOptions {
|
|||
public static final String SHOW_ANIMATION_DESCRIPTION = "Signals to the Function Graph to " +
|
||||
"use animated transitions for certain operations, like navigation.";
|
||||
|
||||
public static final String USE_MOUSE_RELATIVE_ZOOM = "Use Mouse-relative Zoom";
|
||||
public static final String USE_MOUSE_RELATIVE_ZOOM_KEY = "Use Mouse-relative Zoom";
|
||||
public static final String USE_MOUSE_RELATIVE_ZOOM_DESCRIPTION = "When true the Function " +
|
||||
"Graph will perform zoom operations relative to the mouse point.";
|
||||
|
||||
public static final String USE_CONDENSED_LAYOUT = "Use Condensed Layout";
|
||||
public static final String USE_CONDENSED_LAYOUT_KEY = "Use Condensed Layout";
|
||||
public static final String USE_CONDENSED_LAYOUT_DESCRIPTION = "Place vertices as close " +
|
||||
"together as possible. For example, when true, the graph will use little spacing " +
|
||||
"between vertices. Each layout will handle this option differently.";
|
||||
|
@ -37,7 +37,7 @@ public class VisualGraphOptions {
|
|||
"wheel will pan the view vertically. When not enabled, you must hold the <b>" +
|
||||
DockingUtils.CONTROL_KEY_NAME + "</b> key while using the mouse wheel";
|
||||
|
||||
public static final String USE_STICKY_SELECTION = "Use Sticky Selection";
|
||||
public static final String USE_STICKY_SELECTION_KEY = "Use Sticky Selection";
|
||||
public static final String USE_STICKY_SELECTION_DESCRIPTION = "When enabled " +
|
||||
"Selecting code units in one vertex will not clear the selection in another. When " +
|
||||
"disabled, every new selection clears the previous selection <b>unless the Control</b>" +
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
/* ###
|
||||
* 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.graph.viewer.renderer;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
import javax.swing.JComponent;
|
||||
|
||||
import edu.uci.ics.jung.visualization.renderers.DefaultEdgeLabelRenderer;
|
||||
|
||||
/**
|
||||
* Overrides the {@link DefaultEdgeLabelRenderer} so that the client can set the non-picked
|
||||
* foreground color. See {@link #setNonPickedForegroundColor(Color)}.
|
||||
*/
|
||||
public class VisualGraphEdgeLabelRenderer extends DefaultEdgeLabelRenderer {
|
||||
|
||||
private Color nonPickedForegroundColor;
|
||||
|
||||
public VisualGraphEdgeLabelRenderer(Color pickedColor) {
|
||||
super(pickedColor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <E> Component getEdgeLabelRendererComponent(JComponent vv, Object value, Font font,
|
||||
boolean isSelected, E edge) {
|
||||
|
||||
super.getEdgeLabelRendererComponent(vv, value, font, isSelected, edge);
|
||||
|
||||
// fixup the parent call to use this label's foreground
|
||||
if (!isSelected) {
|
||||
setForeground(nonPickedForegroundColor);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the foreground color for this renderer when the edge is not picked/selected
|
||||
*
|
||||
* @param color the color
|
||||
*/
|
||||
public void setNonPickedForegroundColor(Color color) {
|
||||
this.nonPickedForegroundColor = color;
|
||||
}
|
||||
}
|
|
@ -19,6 +19,8 @@ import java.awt.Color;
|
|||
import java.awt.geom.Point2D;
|
||||
import java.util.*;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
|
||||
import edu.uci.ics.jung.algorithms.layout.Layout;
|
||||
import edu.uci.ics.jung.graph.Graph;
|
||||
import edu.uci.ics.jung.visualization.*;
|
||||
|
@ -82,11 +84,10 @@ public class VisualGraphRenderer<V extends VisualVertex, E extends VisualEdge<V>
|
|||
|
||||
// paint all the edges
|
||||
// DEBUG code to show the edges *over* the vertices
|
||||
for (E e : layout.getGraph().getEdges()) {
|
||||
|
||||
renderEdge(renderContext, layout, e);
|
||||
renderEdgeLabel(renderContext, layout, e);
|
||||
}
|
||||
// for (E e : layout.getGraph().getEdges()) {
|
||||
// renderEdge(renderContext, layout, e);
|
||||
// renderEdgeLabel(renderContext, layout, e);
|
||||
// }
|
||||
|
||||
paintLayoutGridCells(renderContext, layout);
|
||||
}
|
||||
|
@ -109,11 +110,13 @@ public class VisualGraphRenderer<V extends VisualVertex, E extends VisualEdge<V>
|
|||
return;
|
||||
}
|
||||
|
||||
String label = rc.getEdgeLabelTransformer().apply(e);
|
||||
Function<? super E, String> xform = rc.getEdgeLabelTransformer();
|
||||
String label = xform.apply(e);
|
||||
if (label == null) {
|
||||
return;
|
||||
}
|
||||
super.renderEdgeLabel(rc, layout, e);
|
||||
|
||||
edgeLabelRenderer.labelEdge(rc, layout, e, xform.apply(e));
|
||||
}
|
||||
|
||||
private void paintLayoutGridCells(RenderContext<V, E> renderContext, Layout<V, E> layout) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue