diff --git a/Ghidra/Features/FunctionGraph/certification.manifest b/Ghidra/Features/FunctionGraph/certification.manifest
index 837c247046..aa2f88ebac 100644
--- a/Ghidra/Features/FunctionGraph/certification.manifest
+++ b/Ghidra/Features/FunctionGraph/certification.manifest
@@ -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|
diff --git a/Ghidra/Features/FunctionGraph/src/main/help/help/TOC_Source.xml b/Ghidra/Features/FunctionGraph/src/main/help/help/TOC_Source.xml
index 79090c4f56..50b978faac 100644
--- a/Ghidra/Features/FunctionGraph/src/main/help/help/TOC_Source.xml
+++ b/Ghidra/Features/FunctionGraph/src/main/help/help/TOC_Source.xml
@@ -63,6 +63,9 @@
The Use Full-size TooltipWhen 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.
+The Use Mouse-relative Zoom 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.
diff --git a/Ghidra/Features/FunctionGraph/src/main/help/help/topics/FunctionGraphPlugin/Function_Graph_Layouts.html b/Ghidra/Features/FunctionGraph/src/main/help/help/topics/FunctionGraphPlugin/Function_Graph_Layouts.html new file mode 100644 index 0000000000..2f2eab68a8 --- /dev/null +++ b/Ghidra/Features/FunctionGraph/src/main/help/help/topics/FunctionGraphPlugin/Function_Graph_Layouts.html @@ -0,0 +1,59 @@ + + + + + + ++Nested Code Layout
+ +++ + +The nested code layout use the + Decompiler 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 +
+if
statement will be nested below and to the right of the code block that + contains the conditional check. The nested code block is dominated 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. +The edges leaving a code block are labeled with the type of high-level conditional + statement (e.g.,
+if
,if/else
, etc) used to determine code flow. +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. +
+ + +Nested Code Layout Options
+ +++ +The Route Edges Around Vertices 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.) +
+Provided by: Function Graph Plugin
+
+ + \ No newline at end of file diff --git a/Ghidra/Features/FunctionGraph/src/main/java/ghidra/app/plugin/core/functiongraph/FGProvider.java b/Ghidra/Features/FunctionGraph/src/main/java/ghidra/app/plugin/core/functiongraph/FGProvider.java index fe477d581e..176d318061 100644 --- a/Ghidra/Features/FunctionGraph/src/main/java/ghidra/app/plugin/core/functiongraph/FGProvider.java +++ b/Ghidra/Features/FunctionGraph/src/main/java/ghidra/app/plugin/core/functiongraph/FGProvider.java @@ -574,6 +574,17 @@ public class FGProvider extends VisualGraphComponentProvider// edge label rendering com.google.common.base.Function 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); diff --git a/Ghidra/Features/FunctionGraph/src/main/java/ghidra/app/plugin/core/functiongraph/graph/layout/FGLayoutOptions.java b/Ghidra/Features/FunctionGraph/src/main/java/ghidra/app/plugin/core/functiongraph/graph/layout/FGLayoutOptions.java index 547e5ca3d4..1d076aa0c4 100644 --- a/Ghidra/Features/FunctionGraph/src/main/java/ghidra/app/plugin/core/functiongraph/graph/layout/FGLayoutOptions.java +++ b/Ghidra/Features/FunctionGraph/src/main/java/ghidra/app/plugin/core/functiongraph/graph/layout/FGLayoutOptions.java @@ -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); } diff --git a/Ghidra/Features/FunctionGraph/src/main/java/ghidra/app/plugin/core/functiongraph/graph/vertex/ListingGraphComponentPanel.java b/Ghidra/Features/FunctionGraph/src/main/java/ghidra/app/plugin/core/functiongraph/graph/vertex/ListingGraphComponentPanel.java index f5bc621092..1d5441d98b 100644 --- a/Ghidra/Features/FunctionGraph/src/main/java/ghidra/app/plugin/core/functiongraph/graph/vertex/ListingGraphComponentPanel.java +++ b/Ghidra/Features/FunctionGraph/src/main/java/ghidra/app/plugin/core/functiongraph/graph/vertex/ListingGraphComponentPanel.java @@ -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 diff --git a/Ghidra/Features/FunctionGraph/src/main/java/ghidra/app/plugin/core/functiongraph/mvc/FunctionGraphOptions.java b/Ghidra/Features/FunctionGraph/src/main/java/ghidra/app/plugin/core/functiongraph/mvc/FunctionGraphOptions.java index 880710d799..16208425f9 100644 --- a/Ghidra/Features/FunctionGraph/src/main/java/ghidra/app/plugin/core/functiongraph/mvc/FunctionGraphOptions.java +++ b/Ghidra/Features/FunctionGraph/src/main/java/ghidra/app/plugin/core/functiongraph/mvc/FunctionGraphOptions.java @@ -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: " + @@ -48,7 +54,7 @@ public class FunctionGraphOptions extends VisualGraphOptions { "
" + "- Block Model Changes Only - relayout the graph when the block model changes " + "(like when a label has been added to the program in the currently graphed function)
" + "- Vertex Grouping Changes Only - when vertices are grouped or ungrouped
" + - "- Never - do not automatically relayout the graph
" + + "Never - do not automatically relayout the graph
" + "See help for more"; 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 MaplayoutOptionsByName = 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 > entries = layoutOptionsByName.entrySet(); + for (Entry 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); } + } diff --git a/Ghidra/Features/FunctionGraphDecompilerExtension/src/main/java/ghidra/app/plugin/core/functiongraph/graph/layout/CodeFlowEdgeLabelRenderer.java b/Ghidra/Features/FunctionGraphDecompilerExtension/src/main/java/ghidra/app/plugin/core/functiongraph/graph/layout/DNLEdgeLabelRenderer.java similarity index 64% rename from Ghidra/Features/FunctionGraphDecompilerExtension/src/main/java/ghidra/app/plugin/core/functiongraph/graph/layout/CodeFlowEdgeLabelRenderer.java rename to Ghidra/Features/FunctionGraphDecompilerExtension/src/main/java/ghidra/app/plugin/core/functiongraph/graph/layout/DNLEdgeLabelRenderer.java index 4861c70a25..20abdb22c2 100644 --- a/Ghidra/Features/FunctionGraphDecompilerExtension/src/main/java/ghidra/app/plugin/core/functiongraph/graph/layout/CodeFlowEdgeLabelRenderer.java +++ b/Ghidra/Features/FunctionGraphDecompilerExtension/src/main/java/ghidra/app/plugin/core/functiongraph/graph/layout/DNLEdgeLabelRenderer.java @@ -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 +/** + * An edge label renderer used with the {@link DecompilerNestedLayout} + * + * @param the vertex type + * @param the edge type + */ +class DNLEdgeLabelRenderer implements Renderer.EdgeLabel { - private static final int EDGE_OFFSET = 20; + private static final int DEFAULT_EDGE_OFFSET = 20; - VisualGraphVertexShapeTransformer vertexShapeTransformer = - new VisualGraphVertexShapeTransformer(); + private VisualGraphVertexShapeTransformer vertexShapeTransformer = + new VisualGraphVertexShapeTransformer<>(); + + private double edgeOffset; + + DNLEdgeLabelRenderer(double condenseFactor) { + this.edgeOffset = DEFAULT_EDGE_OFFSET * (1 - condenseFactor); + } @Override public void labelEdge(RenderContext rc, Layout layout, E e, String text) { - if (text == null || text.isEmpty()) { - return; - } - Graph jungGraph = layout.getGraph(); + if (!rc.getEdgeIncludePredicate().apply(Context.getInstance(jungGraph, e))) { + return; + } + Pair endpoints = jungGraph.getEndpoints(e); - V v1 = endpoints.getFirst(); - V v2 = endpoints.getSecond(); - if (!rc.getEdgeIncludePredicate().apply( - Context. , E> getInstance(jungGraph, e))) { + V startv = endpoints.getFirst(); + V endv = endpoints.getSecond(); + + Predicate , V>> includeVertex = rc.getVertexIncludePredicate(); + if (!includeVertex.apply(Context.getInstance(jungGraph, startv)) || + !includeVertex.apply(Context.getInstance(jungGraph, endv))) { return; } - if (!rc.getVertexIncludePredicate().apply( - Context. , V> getInstance(jungGraph, v1)) || - !rc.getVertexIncludePredicate().apply( - Context. , 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 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 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); } diff --git a/Ghidra/Features/FunctionGraphDecompilerExtension/src/main/java/ghidra/app/plugin/core/functiongraph/graph/layout/DNLayoutOptions.java b/Ghidra/Features/FunctionGraphDecompilerExtension/src/main/java/ghidra/app/plugin/core/functiongraph/graph/layout/DNLayoutOptions.java index fe331c080b..1467a84781 100644 --- a/Ghidra/Features/FunctionGraphDecompilerExtension/src/main/java/ghidra/app/plugin/core/functiongraph/graph/layout/DNLayoutOptions.java +++ b/Ghidra/Features/FunctionGraphDecompilerExtension/src/main/java/ghidra/app/plugin/core/functiongraph/graph/layout/DNLayoutOptions.java @@ -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); + } } diff --git a/Ghidra/Features/FunctionGraphDecompilerExtension/src/main/java/ghidra/app/plugin/core/functiongraph/graph/layout/DecompilerNestedLayout.java b/Ghidra/Features/FunctionGraphDecompilerExtension/src/main/java/ghidra/app/plugin/core/functiongraph/graph/layout/DecompilerNestedLayout.java index bff28d7aaa..89e4706a4d 100644 --- a/Ghidra/Features/FunctionGraphDecompilerExtension/src/main/java/ghidra/app/plugin/core/functiongraph/graph/layout/DecompilerNestedLayout.java +++ b/Ghidra/Features/FunctionGraphDecompilerExtension/src/main/java/ghidra/app/plugin/core/functiongraph/graph/layout/DecompilerNestedLayout.java @@ -102,7 +102,7 @@ public class DecompilerNestedLayout extends AbstractFGLayout { @Override public EdgeLabel getEdgeLabelRenderer() { - return new CodeFlowEdgeLabelRenderer<>(); + return new DNLEdgeLabelRenderer<>(getCondenseFactor()); } @Override diff --git a/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/options/VisualGraphOptions.java b/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/options/VisualGraphOptions.java index a77f3d33e0..458c445409 100644 --- a/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/options/VisualGraphOptions.java +++ b/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/options/VisualGraphOptions.java @@ -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 " + DockingUtils.CONTROL_KEY_NAME + " 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 unless the Control" + diff --git a/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/renderer/VisualGraphEdgeLabelRenderer.java b/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/renderer/VisualGraphEdgeLabelRenderer.java new file mode 100644 index 0000000000..d7fd900edb --- /dev/null +++ b/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/renderer/VisualGraphEdgeLabelRenderer.java @@ -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 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; + } +} diff --git a/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/renderer/VisualGraphRenderer.java b/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/renderer/VisualGraphRenderer.java index 62b14ff04a..e697c9da4d 100644 --- a/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/renderer/VisualGraphRenderer.java +++ b/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/renderer/VisualGraphRenderer.java @@ -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 // 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 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 renderContext, Layout layout) {