mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 18:29:37 +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/undo.png||GHIDRA||reviewed||END|
|
||||||
src/main/help/help/shared/warning.png||Oxygen Icons - LGPL 3.0||||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.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/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_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|
|
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="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="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="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>
|
</tocdef>
|
||||||
</tocref>
|
</tocref>
|
||||||
|
|
||||||
|
|
|
@ -809,6 +809,11 @@
|
||||||
option to fit as many vertices on the screen as possible. Disable this option to make the
|
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>
|
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
|
<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
|
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>
|
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);
|
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
|
@Override
|
||||||
public void domainObjectChanged(DomainObjectChangedEvent ev) {
|
public void domainObjectChanged(DomainObjectChangedEvent ev) {
|
||||||
if (!isVisible()) {
|
if (!isVisible()) {
|
||||||
|
|
|
@ -169,19 +169,23 @@ public class FunctionGraphPlugin extends ProgramPlugin implements OptionsChangeL
|
||||||
@Override
|
@Override
|
||||||
public void optionsChanged(ToolOptions options, String optionName, Object oldValue,
|
public void optionsChanged(ToolOptions options, String optionName, Object oldValue,
|
||||||
Object newValue) {
|
Object newValue) {
|
||||||
functionGraphOptions.loadOptions(options);
|
|
||||||
connectedProvider.getComponent().repaint();
|
|
||||||
for (FGProvider provider : disconnectedProviders) {
|
|
||||||
provider.getComponent().repaint();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (VisualGraphOptions.USE_CONDENSED_LAYOUT.equals(optionName)) {
|
functionGraphOptions.loadOptions(options);
|
||||||
// the condensed setting requires us to reposition the graph
|
|
||||||
|
if (functionGraphOptions.optionChangeRequiresRelayout(optionName)) {
|
||||||
connectedProvider.refreshAndKeepPerspective();
|
connectedProvider.refreshAndKeepPerspective();
|
||||||
}
|
}
|
||||||
else if (VisualGraphOptions.VIEW_RESTORE_OPTIONS_KEY.equals(optionName)) {
|
else if (VisualGraphOptions.VIEW_RESTORE_OPTIONS_KEY.equals(optionName)) {
|
||||||
connectedProvider.clearViewSettings();
|
connectedProvider.clearViewSettings();
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
connectedProvider.refreshDisplayWithoutRebuilding();
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedProvider.getComponent().repaint();
|
||||||
|
for (FGProvider provider : disconnectedProviders) {
|
||||||
|
provider.getComponent().repaint();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -24,7 +24,6 @@ import org.jdom.Element;
|
||||||
import edu.uci.ics.jung.algorithms.layout.Layout;
|
import edu.uci.ics.jung.algorithms.layout.Layout;
|
||||||
import edu.uci.ics.jung.visualization.RenderContext;
|
import edu.uci.ics.jung.visualization.RenderContext;
|
||||||
import edu.uci.ics.jung.visualization.picking.PickedState;
|
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.renderers.Renderer;
|
||||||
import edu.uci.ics.jung.visualization.util.Caching;
|
import edu.uci.ics.jung.visualization.util.Caching;
|
||||||
import ghidra.app.plugin.core.functiongraph.graph.jung.renderer.FGEdgePaintTransformer;
|
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.LayoutListener.ChangeType;
|
||||||
import ghidra.graph.viewer.layout.LayoutProvider;
|
import ghidra.graph.viewer.layout.LayoutProvider;
|
||||||
import ghidra.graph.viewer.layout.VisualGraphLayout;
|
import ghidra.graph.viewer.layout.VisualGraphLayout;
|
||||||
|
import ghidra.graph.viewer.renderer.VisualGraphEdgeLabelRenderer;
|
||||||
import ghidra.program.model.listing.Function;
|
import ghidra.program.model.listing.Function;
|
||||||
import ghidra.program.util.ProgramLocation;
|
import ghidra.program.util.ProgramLocation;
|
||||||
import ghidra.util.SystemUtilities;
|
import ghidra.util.SystemUtilities;
|
||||||
|
@ -208,7 +208,12 @@ public class FGComponent extends GraphComponent<FGVertex, FGEdge, FunctionGraph>
|
||||||
// edge label rendering
|
// edge label rendering
|
||||||
com.google.common.base.Function<FGEdge, String> edgeLabelTransformer = e -> e.getLabel();
|
com.google.common.base.Function<FGEdge, String> edgeLabelTransformer = e -> e.getLabel();
|
||||||
renderContext.setEdgeLabelTransformer(edgeLabelTransformer);
|
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);
|
edgeLabelRenderer.setRotateEdgeLabels(false);
|
||||||
renderContext.setEdgeLabelRenderer(edgeLabelRenderer);
|
renderContext.setEdgeLabelRenderer(edgeLabelRenderer);
|
||||||
|
|
||||||
|
|
|
@ -40,4 +40,13 @@ public interface FGLayoutOptions {
|
||||||
* @param options the tool options
|
* @param options the tool options
|
||||||
*/
|
*/
|
||||||
public void loadOptions(Options 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() {
|
private void createListingPanelToolTipComponent() {
|
||||||
JPanel panel = new JPanel(new BorderLayout());
|
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.setTextBackgroundColor(FGVertex.TOOLTIP_BACKGROUND_COLOR);
|
||||||
// previewListingPanel.getFieldPanel().setSelectionMode( FieldPanel.NO_SELECTION );
|
// previewListingPanel.getFieldPanel().setSelectionMode( FieldPanel.NO_SELECTION );
|
||||||
previewListingPanel.getFieldPanel().setCursorOn(false);
|
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 = new GDLabel();
|
||||||
tooltipTitleLabel.setHorizontalAlignment(SwingConstants.LEADING);
|
tooltipTitleLabel.setHorizontalAlignment(SwingConstants.LEADING);
|
||||||
tooltipTitleLabel.setBackground(FGVertex.TOOLTIP_BACKGROUND_COLOR);
|
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
|
// make sure the title stays up-to-date with the symbol at the start address
|
||||||
title = createTitle();
|
title = createTitle();
|
||||||
genericHeader.setTitle(title);
|
genericHeader.setTitle(title);
|
||||||
|
previewListingPanel = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -41,6 +41,12 @@ public class FunctionGraphOptions extends VisualGraphOptions {
|
||||||
"Edge Color - Unconditional Jump ";
|
"Edge Color - Unconditional Jump ";
|
||||||
private static final String EDGE_COLOR_CONDITIONAL_JUMP_KEY = "Edge Color - Conditional 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_KEY = "Automatic Graph Relayout";
|
||||||
public static final String RELAYOUT_OPTIONS_DESCRIPTION = "Signals to the Function Graph " +
|
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>" +
|
"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 " +
|
"<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>" +
|
"(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>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>";
|
"<b><i>See help for more</i></b>";
|
||||||
|
|
||||||
private static final String DEFAULT_GROUP_BACKGROUND_COLOR_KEY = "Default Group Color";
|
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 unconditionalJumpEdgeHighlightColor = HOVER_HIGHLIGHT_UNCONDITIONAL_COLOR;
|
||||||
private Color conditionalJumpEdgeHighlightColor = HOVER_HIGHLIGHT_CONDITIONAL_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<>();
|
private Map<String, FGLayoutOptions> layoutOptionsByName = new HashMap<>();
|
||||||
|
|
||||||
|
@ -117,6 +125,10 @@ public class FunctionGraphOptions extends VisualGraphOptions {
|
||||||
return relayoutOption;
|
return relayoutOption;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean useFullSizeTooltip() {
|
||||||
|
return useFullSizeTooltip;
|
||||||
|
}
|
||||||
|
|
||||||
public void registerOptions(Options options) {
|
public void registerOptions(Options options) {
|
||||||
|
|
||||||
HelpLocation help = new HelpLocation(OWNER, "Options");
|
HelpLocation help = new HelpLocation(OWNER, "Options");
|
||||||
|
@ -128,10 +140,10 @@ public class FunctionGraphOptions extends VisualGraphOptions {
|
||||||
options.registerOption(SHOW_ANIMATION_OPTIONS_KEY, useAnimation(), help,
|
options.registerOption(SHOW_ANIMATION_OPTIONS_KEY, useAnimation(), help,
|
||||||
SHOW_ANIMATION_DESCRIPTION);
|
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);
|
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);
|
new HelpLocation(OWNER, "Layout_Compressing"), USE_CONDENSED_LAYOUT_DESCRIPTION);
|
||||||
|
|
||||||
options.registerOption(VIEW_RESTORE_OPTIONS_KEY, ViewRestoreOption.START_FULLY_ZOOMED_OUT,
|
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,
|
options.registerOption(UPDATE_GROUP_AND_UNGROUP_COLORS, updateGroupColorsAutomatically,
|
||||||
help, UPDATE_GROUP_AND_UNGROUP_COLORS_DESCRIPTION);
|
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,
|
options.registerOption(EDGE_COLOR_CONDITIONAL_JUMP_KEY, conditionalJumpEdgeColor, help,
|
||||||
"Conditional jump edge color");
|
"Conditional jump edge color");
|
||||||
|
|
||||||
|
@ -191,9 +206,12 @@ public class FunctionGraphOptions extends VisualGraphOptions {
|
||||||
|
|
||||||
useAnimation = options.getBoolean(SHOW_ANIMATION_OPTIONS_KEY, useAnimation);
|
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 =
|
viewRestoreOption =
|
||||||
options.getEnum(VIEW_RESTORE_OPTIONS_KEY, ViewRestoreOption.START_FULLY_ZOOMED_OUT);
|
options.getEnum(VIEW_RESTORE_OPTIONS_KEY, ViewRestoreOption.START_FULLY_ZOOMED_OUT);
|
||||||
|
@ -243,6 +261,22 @@ public class FunctionGraphOptions extends VisualGraphOptions {
|
||||||
return Color.BLACK;
|
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) {
|
public FGLayoutOptions getLayoutOptions(String layoutName) {
|
||||||
return layoutOptionsByName.get(layoutName);
|
return layoutOptionsByName.get(layoutName);
|
||||||
}
|
}
|
||||||
|
@ -250,4 +284,5 @@ public class FunctionGraphOptions extends VisualGraphOptions {
|
||||||
public void setLayoutOptions(String layoutName, FGLayoutOptions options) {
|
public void setLayoutOptions(String layoutName, FGLayoutOptions options) {
|
||||||
layoutOptionsByName.put(layoutName, options);
|
layoutOptionsByName.put(layoutName, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,8 @@ import java.awt.geom.AffineTransform;
|
||||||
import java.awt.geom.Point2D;
|
import java.awt.geom.Point2D;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.google.common.base.Predicate;
|
||||||
|
|
||||||
import edu.uci.ics.jung.algorithms.layout.Layout;
|
import edu.uci.ics.jung.algorithms.layout.Layout;
|
||||||
import edu.uci.ics.jung.graph.Graph;
|
import edu.uci.ics.jung.graph.Graph;
|
||||||
import edu.uci.ics.jung.graph.util.Context;
|
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.app.plugin.core.functiongraph.graph.vertex.FGVertex;
|
||||||
import ghidra.graph.viewer.vertex.VisualGraphVertexShapeTransformer;
|
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> {
|
implements Renderer.EdgeLabel<V, E> {
|
||||||
|
|
||||||
private static final int EDGE_OFFSET = 20;
|
private static final int DEFAULT_EDGE_OFFSET = 20;
|
||||||
|
|
||||||
VisualGraphVertexShapeTransformer vertexShapeTransformer =
|
private VisualGraphVertexShapeTransformer<V> vertexShapeTransformer =
|
||||||
new VisualGraphVertexShapeTransformer();
|
new VisualGraphVertexShapeTransformer<>();
|
||||||
|
|
||||||
|
private double edgeOffset;
|
||||||
|
|
||||||
|
DNLEdgeLabelRenderer(double condenseFactor) {
|
||||||
|
this.edgeOffset = DEFAULT_EDGE_OFFSET * (1 - condenseFactor);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void labelEdge(RenderContext<V, E> rc, Layout<V, E> layout, E e, String text) {
|
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();
|
Graph<V, E> jungGraph = layout.getGraph();
|
||||||
|
if (!rc.getEdgeIncludePredicate().apply(Context.getInstance(jungGraph, e))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Pair<V> endpoints = jungGraph.getEndpoints(e);
|
Pair<V> endpoints = jungGraph.getEndpoints(e);
|
||||||
V v1 = endpoints.getFirst();
|
V startv = endpoints.getFirst();
|
||||||
V v2 = endpoints.getSecond();
|
V endv = endpoints.getSecond();
|
||||||
if (!rc.getEdgeIncludePredicate().apply(
|
|
||||||
Context.<Graph<V, E>, E> getInstance(jungGraph, e))) {
|
Predicate<Context<Graph<V, E>, V>> includeVertex = rc.getVertexIncludePredicate();
|
||||||
|
if (!includeVertex.apply(Context.getInstance(jungGraph, startv)) ||
|
||||||
|
!includeVertex.apply(Context.getInstance(jungGraph, endv))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!rc.getVertexIncludePredicate().apply(
|
Point2D start = layout.apply(startv);
|
||||||
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);
|
|
||||||
MultiLayerTransformer multiLayerTransformer = rc.getMultiLayerTransformer();
|
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();
|
Rectangle vertexBounds = vertexShape.getBounds();
|
||||||
int xDisplacement = rc.getLabelOffset();
|
int xDisplacement = rc.getLabelOffset();
|
||||||
|
|
||||||
Point2D labelPointOffset = new Point2D.Double();
|
Point2D labelPointOffset = new Point2D.Double();
|
||||||
|
|
||||||
|
// note: location is centered
|
||||||
|
double cx = start.getX();
|
||||||
|
double cy = start.getY();
|
||||||
|
|
||||||
List<Point2D> articulationPoints = e.getArticulationPoints();
|
List<Point2D> articulationPoints = e.getArticulationPoints();
|
||||||
if (articulationPoints.isEmpty()) {
|
if (articulationPoints.isEmpty()) {
|
||||||
double vertexBottom = p1.getY() + (vertexBounds.height >> 1); // location is centered
|
double vertexBottom = start.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) (p1.getX() + xDisplacement); // right of the edge
|
int textX = (int) (start.getX() + xDisplacement); // right of the edge
|
||||||
labelPointOffset.setLocation(textX, textY);
|
labelPointOffset.setLocation(textX, textY);
|
||||||
}
|
}
|
||||||
else if (articulationPoints.size() == 1) {
|
else if (articulationPoints.size() == 1) {
|
||||||
|
@ -91,24 +104,25 @@ class CodeFlowEdgeLabelRenderer<V extends FGVertex, E extends FGEdge>
|
||||||
Point2D bend2 = articulationPoints.get(1);
|
Point2D bend2 = articulationPoints.get(1);
|
||||||
bend2 = multiLayerTransformer.transform(Layer.LAYOUT, bend2);
|
bend2 = multiLayerTransformer.transform(Layer.LAYOUT, bend2);
|
||||||
|
|
||||||
|
double vertexSide = cx + (vertexBounds.width >> 1);
|
||||||
|
double vertexBottom = cy + (vertexBounds.height >> 1);
|
||||||
|
|
||||||
double bx1 = bend1.getX();
|
double bx1 = bend1.getX();
|
||||||
|
|
||||||
if (articulationPoints.size() == 2) {
|
if (articulationPoints.size() == 2) {
|
||||||
|
|
||||||
double vertexSide = p1.getX() + (vertexBounds.width >> 1); // location is centered
|
int textX = (int) (vertexSide + edgeOffset); // right of the vertex
|
||||||
int textX = (int) (vertexSide + EDGE_OFFSET); // right of the vertex
|
int textY = (int) (cy + edgeOffset); // above the edge
|
||||||
int textY = (int) (p1.getY() + EDGE_OFFSET); // above the edge
|
|
||||||
labelPointOffset.setLocation(textX, textY);
|
labelPointOffset.setLocation(textX, textY);
|
||||||
}
|
}
|
||||||
else if (articulationPoints.size() == 3) {
|
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
|
int textX = (int) (bx1 + xDisplacement); // right of the edge
|
||||||
labelPointOffset.setLocation(textX, textY);
|
labelPointOffset.setLocation(textX, textY);
|
||||||
}
|
}
|
||||||
else if (articulationPoints.size() == 4) {
|
else { // if (articulationPoints.size() == 4) {
|
||||||
double vertexBottom = p1.getY() + (vertexBounds.height >> 1); // location is centered
|
int textY = (int) (vertexBottom + edgeOffset); // below the vertex; above the bend
|
||||||
int textY = (int) (vertexBottom + EDGE_OFFSET); // below the vertex; above the bend
|
|
||||||
int textX = (int) (bx1 + xDisplacement); // right of the edge
|
int textX = (int) (bx1 + xDisplacement); // right of the edge
|
||||||
labelPointOffset.setLocation(textX, textY);
|
labelPointOffset.setLocation(textX, textY);
|
||||||
}
|
}
|
|
@ -23,6 +23,8 @@ import ghidra.util.HelpLocation;
|
||||||
*/
|
*/
|
||||||
public class DNLayoutOptions implements FGLayoutOptions {
|
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 =
|
private static final String USE_EDGE_ROUTING_AROUND_VERTICES_KEY =
|
||||||
"Route Edges Around Vertices";
|
"Route Edges Around Vertices";
|
||||||
private static final String USE_EDGE_ROUTING_AROUND_VERTICES_DESCRIPTION = "Signals that " +
|
private static final String USE_EDGE_ROUTING_AROUND_VERTICES_DESCRIPTION = "Signals that " +
|
||||||
|
@ -34,8 +36,7 @@ public class DNLayoutOptions implements FGLayoutOptions {
|
||||||
@Override
|
@Override
|
||||||
public void registerOptions(Options options) {
|
public void registerOptions(Options options) {
|
||||||
|
|
||||||
// TODO layout-specific help
|
HelpLocation help = new HelpLocation(OWNER, HELP_ANCHOR);
|
||||||
HelpLocation help = new HelpLocation(OWNER, "Options");
|
|
||||||
|
|
||||||
options.registerOption(USE_EDGE_ROUTING_AROUND_VERTICES_KEY, useEdgeRoutingAroundVertices,
|
options.registerOption(USE_EDGE_ROUTING_AROUND_VERTICES_KEY, useEdgeRoutingAroundVertices,
|
||||||
help, USE_EDGE_ROUTING_AROUND_VERTICES_DESCRIPTION);
|
help, USE_EDGE_ROUTING_AROUND_VERTICES_DESCRIPTION);
|
||||||
|
@ -50,4 +51,10 @@ public class DNLayoutOptions implements FGLayoutOptions {
|
||||||
public boolean useEdgeRoutingAroundVertices() {
|
public boolean useEdgeRoutingAroundVertices() {
|
||||||
return 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
|
@Override
|
||||||
public EdgeLabel<FGVertex, FGEdge> getEdgeLabelRenderer() {
|
public EdgeLabel<FGVertex, FGEdge> getEdgeLabelRenderer() {
|
||||||
return new CodeFlowEdgeLabelRenderer<>();
|
return new DNLEdgeLabelRenderer<>(getCondenseFactor());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -23,11 +23,11 @@ public class VisualGraphOptions {
|
||||||
public static final String SHOW_ANIMATION_DESCRIPTION = "Signals to the Function Graph to " +
|
public static final String SHOW_ANIMATION_DESCRIPTION = "Signals to the Function Graph to " +
|
||||||
"use animated transitions for certain operations, like navigation.";
|
"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 " +
|
public static final String USE_MOUSE_RELATIVE_ZOOM_DESCRIPTION = "When true the Function " +
|
||||||
"Graph will perform zoom operations relative to the mouse point.";
|
"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 " +
|
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 " +
|
"together as possible. For example, when true, the graph will use little spacing " +
|
||||||
"between vertices. Each layout will handle this option differently.";
|
"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>" +
|
"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";
|
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 " +
|
public static final String USE_STICKY_SELECTION_DESCRIPTION = "When enabled " +
|
||||||
"Selecting code units in one vertex will not clear the selection in another. When " +
|
"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>" +
|
"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.awt.geom.Point2D;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
import com.google.common.base.Function;
|
||||||
|
|
||||||
import edu.uci.ics.jung.algorithms.layout.Layout;
|
import edu.uci.ics.jung.algorithms.layout.Layout;
|
||||||
import edu.uci.ics.jung.graph.Graph;
|
import edu.uci.ics.jung.graph.Graph;
|
||||||
import edu.uci.ics.jung.visualization.*;
|
import edu.uci.ics.jung.visualization.*;
|
||||||
|
@ -82,11 +84,10 @@ public class VisualGraphRenderer<V extends VisualVertex, E extends VisualEdge<V>
|
||||||
|
|
||||||
// paint all the edges
|
// paint all the edges
|
||||||
// DEBUG code to show the edges *over* the vertices
|
// DEBUG code to show the edges *over* the vertices
|
||||||
for (E e : layout.getGraph().getEdges()) {
|
// for (E e : layout.getGraph().getEdges()) {
|
||||||
|
// renderEdge(renderContext, layout, e);
|
||||||
renderEdge(renderContext, layout, e);
|
// renderEdgeLabel(renderContext, layout, e);
|
||||||
renderEdgeLabel(renderContext, layout, e);
|
// }
|
||||||
}
|
|
||||||
|
|
||||||
paintLayoutGridCells(renderContext, layout);
|
paintLayoutGridCells(renderContext, layout);
|
||||||
}
|
}
|
||||||
|
@ -109,11 +110,13 @@ public class VisualGraphRenderer<V extends VisualVertex, E extends VisualEdge<V>
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String label = rc.getEdgeLabelTransformer().apply(e);
|
Function<? super E, String> xform = rc.getEdgeLabelTransformer();
|
||||||
|
String label = xform.apply(e);
|
||||||
if (label == null) {
|
if (label == null) {
|
||||||
return;
|
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) {
|
private void paintLayoutGridCells(RenderContext<V, E> renderContext, Layout<V, E> layout) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue