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:
dragonmacher 2019-07-26 17:15:22 -04:00
parent a04185e942
commit 17b8e54f25
16 changed files with 289 additions and 63 deletions

View file

@ -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|

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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()) {

View file

@ -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

View file

@ -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);

View file

@ -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);
}

View file

@ -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

View file

@ -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);
}
}

View file

@ -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);
}

View file

@ -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);
}
}

View file

@ -102,7 +102,7 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
@Override
public EdgeLabel<FGVertex, FGEdge> getEdgeLabelRenderer() {
return new CodeFlowEdgeLabelRenderer<>();
return new DNLEdgeLabelRenderer<>(getCondenseFactor());
}
@Override

View file

@ -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>" +

View file

@ -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;
}
}

View file

@ -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) {