diff --git a/Ghidra/Features/Base/certification.manifest b/Ghidra/Features/Base/certification.manifest index 386226b0b2..5ce773a23f 100644 --- a/Ghidra/Features/Base/certification.manifest +++ b/Ghidra/Features/Base/certification.manifest @@ -443,6 +443,8 @@ src/main/help/help/topics/GhidraServer/GhidraServer.htm||GHIDRA||||END| src/main/help/help/topics/Glossary/glossary.htm||GHIDRA||||END| src/main/help/help/topics/Glossary/images/BigEndian.png||GHIDRA||reviewed||END| src/main/help/help/topics/Glossary/images/LittleEndian.png||GHIDRA||reviewed||END| +src/main/help/help/topics/Graph/GraphIntro.html||GHIDRA||||END| +src/main/help/help/topics/Graph/GraphServicesIntro.html||GHIDRA||||END| src/main/help/help/topics/HeadlessAnalyzer/HeadlessAnalyzer.htm||GHIDRA||||END| src/main/help/help/topics/ImporterPlugin/images/About_pdb.png||GHIDRA||reviewed||END| src/main/help/help/topics/ImporterPlugin/images/BatchImportDialog.png||GHIDRA||||END| diff --git a/Ghidra/Features/Base/src/main/help/help/TOC_Source.xml b/Ghidra/Features/Base/src/main/help/help/TOC_Source.xml index 9a27608027..803c9e2aac 100644 --- a/Ghidra/Features/Base/src/main/help/help/TOC_Source.xml +++ b/Ghidra/Features/Base/src/main/help/help/TOC_Source.xml @@ -150,7 +150,9 @@ - + + + diff --git a/Ghidra/Features/Base/src/main/help/help/topics/Graph/GraphIntro.html b/Ghidra/Features/Base/src/main/help/help/topics/Graph/GraphIntro.html new file mode 100644 index 0000000000..21530cd2f8 --- /dev/null +++ b/Ghidra/Features/Base/src/main/help/help/topics/Graph/GraphIntro.html @@ -0,0 +1,24 @@ + + + + + Graphing + + + + + + + +

Graphing

+ +
+

+ This section contains all help related to the creation and display of Graphs. Content will + appear inside of this section as plugins are added. To see the available graph features, + see the Graph menu on the toolbar. +

+
+ + + \ No newline at end of file diff --git a/Ghidra/Features/Base/src/main/help/help/topics/Graph/GraphServicesIntro.html b/Ghidra/Features/Base/src/main/help/help/topics/Graph/GraphServicesIntro.html new file mode 100644 index 0000000000..201f64dc74 --- /dev/null +++ b/Ghidra/Features/Base/src/main/help/help/topics/Graph/GraphServicesIntro.html @@ -0,0 +1,25 @@ + + + + + Graph Services + + + + + + + +

Graph Services

+ +
+

+ This section contains all help related to the graph services that provide capabilities for + generated graphs, such as displaying and exporting. Content will appear inside of this ' + section as plugins are added. To see the available graph features, + see the Graph menu on the toolbar. +

+
+ + + \ No newline at end of file diff --git a/Ghidra/Features/GraphServices/src/main/help/help/TOC_Source.xml b/Ghidra/Features/GraphServices/src/main/help/help/TOC_Source.xml index 372d07792a..4bc4df43ef 100644 --- a/Ghidra/Features/GraphServices/src/main/help/help/TOC_Source.xml +++ b/Ghidra/Features/GraphServices/src/main/help/help/TOC_Source.xml @@ -50,11 +50,9 @@ - - - - - + + + diff --git a/Ghidra/Features/GraphServices/src/main/help/help/topics/GraphServices/GraphDisplay.htm b/Ghidra/Features/GraphServices/src/main/help/help/topics/GraphServices/GraphDisplay.htm index 651c7b122c..ea093ef790 100644 --- a/Ghidra/Features/GraphServices/src/main/help/help/topics/GraphServices/GraphDisplay.htm +++ b/Ghidra/Features/GraphServices/src/main/help/help/topics/GraphServices/GraphDisplay.htm @@ -5,103 +5,245 @@ - Graphing + Graph Display - + + +

Default Graph Display

-

Visualization of a Graph

-
-

The visualization display will show the graph in a new window or in a new tab of a previously created graph window.

-
-
-

-
-
-
-

Manipulating the Graph:

-
    -
  • MouseButton1+drag will translate the display in the x and y axis
  • -
  • Mouse Wheel will zoom in and out
  • -
  • CTRL+Mouse Wheel will zoom in and out in the X-Axis only
  • -
  • ALT+Mouse Wheel will zoom in and out in the Y-Axis only
  • -
  • Ctrl+MouseButton1 will select a vertex or edge
  • -
      -
    • Shift+Ctrl+MouseButton1 over an unselected vertex will add that vertex to the selection
    • -
    • Shift+Ctrl+MouseButton1 over a previously selected vertex will remove that vertex from the selection
    • -
    -
  • Ctrl+MouseButton1+drag on an empty area will create a rectangular area and select enclosed vertices
  • -
  • Ctrl+MouseButton1+drag over a vertex will reposition all selected vertices
  • -
-

Toolbar Buttons

-

The toggle button, when 'set' will cause a focused vertex (red arrow) to be scrolled to the center of the view

-

The toggle button, when 'set' will allow the user to draw a free-form shape that encloses the vertices they wish to select.

-

The toggle button, when 'set' will open a satellite mini view of the graph in the lower right corner. The mini-view can be manipulated with the mouse to affect the main view

-

The button will reset any visual transformations on the graph and center it at a best-effort size

-

The toggle button, when 'set' will open a rectangular magnification lens in the graph view

-
-
    -
  • MouseButton1 click-drag on the lens center circle to move the magnifier lens
  • -
  • MouseButton1 click-draw on a lens edge diamond to resize the magnifier lens
  • -
  • MouseButton1 click on the upper-right circle-cross to dispose of the magnifier lens
  • -
  • MouseWheel will change the magnification of the lens
  • -
-
- -

The button will open a Filter dialog. Select buttons in the dialog to hide specific vertices or edges in the display. - The Filter dialog buttons are created by examining the graph vertex/edge properties to discover candidates for filtering.

- -

The Arrangement menu is used to select one of several graph layout algorithms.

-
+
+

The visualization display will show the graph in a new window or in a new tab of a + previously created graph window.

-
-
-

Popup Actions

-
-

Standard Popup Actions

-
-

Vertex Popup Actions

-
- -

Edge Popup Actions

-
+
+ + + + +
+
+ +

Manipulating the Graph

+ +
    +
  • Dragging in the graph or on any unselected vertices will pan the graph (translate the + display in the x and y axis)
  • + +
  • Dragging a selected vertex will reposition all selected vertices
  • + +
  • Using the Mouse Wheel will zoom the graph in and out
  • + +
  • Control+Mouse Wheel will zoom the graph in and out on the X-Axis only
  • + +
  • ALT+Mouse Wheel will zoom the graph in and out in the Y-Axis only
  • + +
  • Ctrl+Click will select a vertex +
      +
    • Ctrl+Click over an unselected vertex will add that vertex to the + selection
    • + +
    • Ctrl+Click over a previously selected vertex will remove that vertex + from the selection
    • +
    +
  • + +
  • Ctrl+drag on an empty area will create a rectangular area and select + enclosed vertices
  • + + +
+ +

Toolbar Buttons

+ +

+ The toggle button, when 'set' will cause a focused + vertex (the vertex with the red arrow) to be moved to the center of the view

+ +

+ The toggle button, when 'set' will + allow the user to draw a free-form shape that encloses the vertices they wish to select.

+ +

+ The toggle button, + when 'set' will open a satellite mini view of the graph in the lower right corner. The + mini-view can be manipulated with the mouse to affect the main view

+ +

+ The button will reset any visual transformations on the + graph and center it at a best-effort size

+ +

+ The toggle button, when 'set' will open a rectangular + magnification lens in the graph view

+ +
+
+
    +
  • MouseButton1 click-drag on the lens center circle to move the magnifier lens
  • + +
  • MouseButton1 click-draw on a lens edge diamond to resize the magnifier lens
  • + +
  • MouseButton1 click on the upper-right circle-cross to dispose of the magnifier + lens
  • + +
  • MouseWheel will change the magnification of the lens
  • +
+
+
+ +

+ The button will open a Filter dialog. Select + buttons in the dialog to hide specific vertices or edges in the display. The Filter dialog + buttons are created by examining the graph vertex/edge properties to discover candidates for + filtering.

+ +

+ The Arrangement menu is used to + select one of several graph layout algorithms.

+ +
+
+ +
+
+ +

Popup Actions

+ +
+

Standard Popup Actions

+ +
+ +

Vertex Popup Actions

+ +
+ +

Edge Popup Actions

+ +
+
+ +

Provided By:  GraphDisplayBrokerPlugin

+ +

Related Topics:

+ +

+
- \ No newline at end of file + diff --git a/Ghidra/Features/GraphServices/src/main/help/help/topics/GraphServices/GraphExport.htm b/Ghidra/Features/GraphServices/src/main/help/help/topics/GraphServices/GraphExport.htm index d6080a0adc..e0e6a5d3b3 100644 --- a/Ghidra/Features/GraphServices/src/main/help/help/topics/GraphServices/GraphExport.htm +++ b/Ghidra/Features/GraphServices/src/main/help/help/topics/GraphServices/GraphExport.htm @@ -11,19 +11,22 @@ - +

Graph Export Service

Export Dialog

Whenever a graph is generated and the graph output is set to Graph Export, then the following graph export dialog is displayed:


-
-
-

-
-
+
+ + + + +
+

The Export Graph dialog offers a choice of the following graph formats:

@@ -49,5 +52,16 @@

The Ok button will marshal the graph to the selected file in the selected format and close the dialog.

The Cancel button will close the dialog and perform no other action.

+ + +

Provided By:  GraphDisplayBrokerPlugin

+ +

Related Topics:

+
+
+
+ \ No newline at end of file diff --git a/Ghidra/Features/GraphServices/src/main/help/help/topics/GraphServices/images/DefaultGraphDisplay.png b/Ghidra/Features/GraphServices/src/main/help/help/topics/GraphServices/images/DefaultGraphDisplay.png index e0b1d7c5ea..c39a2cdf1e 100644 Binary files a/Ghidra/Features/GraphServices/src/main/help/help/topics/GraphServices/images/DefaultGraphDisplay.png and b/Ghidra/Features/GraphServices/src/main/help/help/topics/GraphServices/images/DefaultGraphDisplay.png differ diff --git a/Ghidra/Features/GraphServices/src/main/help/help/topics/GraphServices/images/ExportDialog.png b/Ghidra/Features/GraphServices/src/main/help/help/topics/GraphServices/images/ExportDialog.png index 7bf7f1a8a2..089efa0baa 100644 Binary files a/Ghidra/Features/GraphServices/src/main/help/help/topics/GraphServices/images/ExportDialog.png and b/Ghidra/Features/GraphServices/src/main/help/help/topics/GraphServices/images/ExportDialog.png differ diff --git a/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/export/ExportAttributedGraphDisplayProvider.java b/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/export/ExportAttributedGraphDisplayProvider.java index 218c5c51d8..92595e073d 100644 --- a/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/export/ExportAttributedGraphDisplayProvider.java +++ b/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/export/ExportAttributedGraphDisplayProvider.java @@ -73,6 +73,6 @@ public class ExportAttributedGraphDisplayProvider implements GraphDisplayProvide @Override public HelpLocation getHelpLocation() { - return new HelpLocation("GraphServices", "Default_Graph_Exporter"); + return new HelpLocation("GraphServices", "Graph_Exporter"); } } diff --git a/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/export/GraphExporterDialog.java b/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/export/GraphExporterDialog.java index f12dd24485..15c282c8a9 100644 --- a/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/export/GraphExporterDialog.java +++ b/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/export/GraphExporterDialog.java @@ -74,7 +74,7 @@ public class GraphExporterDialog extends DialogComponentProvider { addWorkPanel(buildWorkPanel()); addOKButton(); addCancelButton(); - setHelpLocation(new HelpLocation("ExporterPlugin", "Exporter_Dialog")); + setHelpLocation(new HelpLocation("GraphServices", "Graph_Exporter")); validate(); } diff --git a/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/DefaultDisplayGraphIcons.java b/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/DefaultDisplayGraphIcons.java index c63bcd5b5e..d90ae4628d 100644 --- a/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/DefaultDisplayGraphIcons.java +++ b/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/DefaultDisplayGraphIcons.java @@ -27,10 +27,10 @@ final class DefaultDisplayGraphIcons { private DefaultDisplayGraphIcons() { } - public static final Icon SATELLITE_VIEW_ICON = Icons.get("images/sat2.png"); + public static final Icon SATELLITE_VIEW_ICON = Icons.get("images/network-wireless-16.png"); public static final Icon VIEW_MAGNIFIER_ICON = Icons.get("images/magnifier.png"); public static final Icon PROGRAM_GRAPH_ICON = Icons.get("images/redspheregraph.png"); public static final Icon LAYOUT_ALGORITHM_ICON = Icons.get("images/katomic.png"); public static final Icon LASSO_ICON = Icons.get("images/Lasso.png"); - public static final Icon FILTER_ICON = Icons.get("images/filter_on.png"); + public static final Icon FILTER_ICON = Icons.CONFIGURE_FILTER_ICON; } diff --git a/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/DefaultGraphDisplay.java b/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/DefaultGraphDisplay.java index 69f88d0c47..f665f710c0 100644 --- a/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/DefaultGraphDisplay.java +++ b/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/DefaultGraphDisplay.java @@ -23,7 +23,6 @@ import java.awt.event.*; import java.awt.geom.Point2D; import java.util.*; import java.util.List; -import java.util.function.Consumer; import java.util.function.Predicate; import java.util.logging.Logger; import java.util.stream.Collectors; @@ -64,6 +63,8 @@ import ghidra.framework.plugintool.PluginTool; import ghidra.graph.AttributeFilters; import ghidra.graph.job.GraphJobRunner; import ghidra.graph.viewer.popup.*; +import ghidra.graph.visualization.mouse.JgtPluggableGraphMouse; +import ghidra.graph.visualization.mouse.JgtUtils; import ghidra.service.graph.*; import ghidra.util.*; import ghidra.util.exception.CancelledException; @@ -76,19 +77,18 @@ import resources.Icons; */ public class DefaultGraphDisplay implements GraphDisplay { - public static final String FAVORED_EDGE = "Fall-Through"; - private static final int MAX_NODES = Integer.getInteger("maxNodes", 10000); - public static final Dimension PREFERRED_VIEW_SIZE = new Dimension(1000, 1000); - public static final Dimension PREFERRED_LAYOUT_SIZE = new Dimension(3000, 3000); + private static final String ACTION_OWNER = "GraphServices"; - Logger log = Logger.getLogger(DefaultGraphDisplay.class.getName()); + private static final String FAVORED_EDGE = "Fall-Through"; + private static final int MAX_NODES = Integer.getInteger("maxNodes", 10000); + private static final Dimension PREFERRED_VIEW_SIZE = new Dimension(1000, 1000); + private static final Dimension PREFERRED_LAYOUT_SIZE = new Dimension(3000, 3000); + + private Logger log = Logger.getLogger(DefaultGraphDisplay.class.getName()); private GraphDisplayListener listener = new DummyGraphDisplayListener(); private String title; - /** - * the {@link Graph} to visualize - */ private AttributedGraph graph; /** @@ -97,91 +97,64 @@ public class DefaultGraphDisplay implements GraphDisplay { private final int displayId; /** - * the delegate viewer to display the ProgramGraph + * The delegate viewer to display the ProgramGraph */ private final VisualizationViewer viewer; /** - * the {@link PluginTool} + * The {@link PluginTool} */ private final PluginTool pluginTool; - /** - * the "owner name" for action - mainly affects default help location - */ - private final String actionOwnerName = "GraphServices"; - - /** - * provides the component for the {@link GraphDisplay} - */ private final DefaultGraphDisplayComponentProvider componentProvider; /** - * whether to ensure the focused vertex is visible, scrolling if necessary + * Whether to ensure the focused vertex is visible, scrolling if necessary * the visualization in order to center the selected vertex * or the center of the set of selected vertices */ private boolean ensureVertexIsVisible = false; /** - * allows selection of various {@link LayoutAlgorithm} ('arrangements') + * Allows selection of various {@link LayoutAlgorithm} ('arrangements') */ private final LayoutTransitionManager layoutTransitionManager; /** - * provides graph displays for supplied graphs + * Provides graph displays for supplied graphs */ private final DefaultGraphDisplayProvider graphDisplayProvider; /** * the vertex that has been nominated to be 'focused' in the graph display and listing */ private AttributedVertex focusedVertex; + + /** + * Runs animation jobs for updating the display + */ private final GraphJobRunner jobRunner = new GraphJobRunner(); + /** * a satellite view that shows in the lower left corner as a birds-eye view of the graph display */ private final SatelliteVisualizationViewer satelliteViewer; - /** - * generated filters on edges - */ - private AttributeFilters edgeFilters; - /** - * generated filters on vertices - */ - private AttributeFilters vertexFilters; - /** - * a dialog populated with generated vertex/edge filters - */ + private FilterDialog filterDialog; - /** - * holds the vertex icons (instead of recomputing them) - */ + private AttributeFilters edgeFilters; + private AttributeFilters vertexFilters; + private GhidraIconCache iconCache; + /** - * multi-selection is done in a free-form traced shape instead of a rectangle + * Multi-selection is done in a free-form traced shape instead of a rectangle */ private boolean freeFormSelection; /** - * Handles the popup + * Handles all mouse interaction */ - private GhidraGraphMouse graphMouse; + private JgtPluggableGraphMouse graphMouse; - /** - * Will accept a {@link Graph} and use it to create a new graph display in - * a new tab or new window - */ - Consumer> subgraphConsumer = g -> { - - AttributedGraph attributedGraph = new AttributedGraph(); - g.vertexSet().forEach(attributedGraph::addVertex); - g.edgeSet().forEach(e -> { - AttributedVertex source = g.getEdgeSource(e); - AttributedVertex target = g.getEdgeTarget(e); - attributedGraph.addEdge(source, target, e); - }); - displaySubGraph(attributedGraph); - }; private ToggleDockingAction hideSelectedAction; private ToggleDockingAction hideUnselectedAction; private SwitchableSelectionItemListener switchableSelectionListener; @@ -226,7 +199,7 @@ public class DefaultGraphDisplay implements GraphDisplay { .builder(viewer.getRenderContext().getVertexBoundsFunction()) .build()); - graphMouse = new GhidraGraphMouse(componentProvider, viewer); + graphMouse = new JgtPluggableGraphMouse(this); createToolbarActions(); createPopupActions(); @@ -302,7 +275,7 @@ public class DefaultGraphDisplay implements GraphDisplay { private void createToolbarActions() { // create a toggle for 'scroll to selected vertex' - new ToggleActionBuilder("Scroll To Selection", actionOwnerName) + new ToggleActionBuilder("Scroll To Selection", ACTION_OWNER) .toolBarIcon(Icons.NAVIGATE_ON_INCOMING_EVENT_ICON) .description("Ensure that the 'focused' vertex is visible") .selected(true) @@ -314,7 +287,7 @@ public class DefaultGraphDisplay implements GraphDisplay { // create a toggle for enabling 'free-form' selection: selection is // inside of a traced shape instead of a rectangle - new ToggleActionBuilder("Free-Form Selection", actionOwnerName) + new ToggleActionBuilder("Free-Form Selection", ACTION_OWNER) .toolBarIcon(DefaultDisplayGraphIcons.LASSO_ICON) .description("Trace Free-Form Shape to select multiple vertices (CTRL-click-drag)") .selected(false) @@ -323,14 +296,14 @@ public class DefaultGraphDisplay implements GraphDisplay { .buildAndInstallLocal(componentProvider); // create an icon button to display the satellite view - new ToggleActionBuilder("SatelliteView", actionOwnerName).description("Show Satellite View") + new ToggleActionBuilder("SatelliteView", ACTION_OWNER).description("Show Satellite View") .toolBarIcon(DefaultDisplayGraphIcons.SATELLITE_VIEW_ICON) .onAction(this::toggleSatellite) .selected(graphDisplayProvider.getDefaultSatelliteState()) .buildAndInstallLocal(componentProvider); // create an icon button to reset the view transformations to identity (scaled to layout) - new ActionBuilder("Reset View", actionOwnerName) + new ActionBuilder("Reset View", ACTION_OWNER) .description("Reset all view transforms to center graph in display") .toolBarIcon(Icons.REFRESH_ICON) .onAction(context -> viewer.scaleToLayout()) @@ -338,7 +311,7 @@ public class DefaultGraphDisplay implements GraphDisplay { // create a button to show the view magnify lens LensSupport magnifyViewSupport = createMagnifier(); - ToggleDockingAction lensToggle = new ToggleActionBuilder("View Magnifier", actionOwnerName) + ToggleDockingAction lensToggle = new ToggleActionBuilder("View Magnifier", ACTION_OWNER) .description("Show View Magnifier") .toolBarIcon(DefaultDisplayGraphIcons.VIEW_MAGNIFIER_ICON) .onAction(context -> magnifyViewSupport.activate( @@ -349,90 +322,91 @@ public class DefaultGraphDisplay implements GraphDisplay { componentProvider.addLocalAction(lensToggle); // create an action button to show a dialog with generated filters - new ActionBuilder("Show Filters", actionOwnerName).description("Show Graph Filters") + new ActionBuilder("Show Filters", ACTION_OWNER).description("Show Graph Filters") .toolBarIcon(DefaultDisplayGraphIcons.FILTER_ICON) .onAction(context -> showFilterDialog()) .buildAndInstallLocal(componentProvider); // create a menu with graph layout algorithm selections - new MultiStateActionBuilder("Arrangement", actionOwnerName) - .description("Select Layout Arrangement Algorithm") + List> layoutActionStates = getLayoutActionStates(); + new MultiStateActionBuilder("Arrangement", ACTION_OWNER) + .description("Arrangement: " + layoutActionStates.get(0).getName()) .toolBarIcon(DefaultDisplayGraphIcons.LAYOUT_ALGORITHM_ICON) .fireFirstAction(false) .onActionStateChanged((s, t) -> layoutChanged(s.getName())) - .addStates(getLayoutActionStates()) + .addStates(layoutActionStates) .buildAndInstallLocal(componentProvider); } private void createPopupActions() { - new ActionBuilder("Select Vertex", actionOwnerName) + new ActionBuilder("Select Vertex", ACTION_OWNER) .popupMenuPath("Select Vertex") .popupMenuGroup("selection", "1") .withContext(VertexGraphActionContext.class) - .enabledWhen(c -> !viewer.getSelectedVertexState().isSelected(c.getClickedVertex())) + .enabledWhen(c -> !isSelected(c.getClickedVertex())) .onAction(c -> viewer.getSelectedVertexState().select(c.getClickedVertex())) .buildAndInstallLocal(componentProvider); - new ActionBuilder("Deselect Vertex", actionOwnerName) + new ActionBuilder("Deselect Vertex", ACTION_OWNER) .popupMenuPath("Deselect Vertex") .popupMenuGroup("selection", "2") .withContext(VertexGraphActionContext.class) - .enabledWhen(c -> viewer.getSelectedVertexState().isSelected(c.getClickedVertex())) + .enabledWhen(c -> isSelected(c.getClickedVertex())) .onAction(c -> viewer.getSelectedVertexState().deselect(c.getClickedVertex())) .buildAndInstallLocal(componentProvider); - new ActionBuilder("Select Edge", actionOwnerName) + new ActionBuilder("Select Edge", ACTION_OWNER) .popupMenuPath("Select Edge") .popupMenuGroup("selection", "1") .withContext(EdgeGraphActionContext.class) - .enabledWhen(c -> !viewer.getSelectedEdgeState().isSelected(c.getClickedEdge())) + .enabledWhen(c -> !isSelected(c.getClickedEdge())) .onAction(c -> selectEdge(c.getClickedEdge())) .buildAndInstallLocal(componentProvider); - new ActionBuilder("Deselect Edge", actionOwnerName) + new ActionBuilder("Deselect Edge", ACTION_OWNER) .popupMenuPath("Deselect Edge") .popupMenuGroup("selection", "2") .withContext(EdgeGraphActionContext.class) - .enabledWhen(c -> viewer.getSelectedEdgeState().isSelected(c.getClickedEdge())) + .enabledWhen(c -> isSelected(c.getClickedEdge())) .onAction(c -> deselectEdge(c.getClickedEdge())) .buildAndInstallLocal(componentProvider); - new ActionBuilder("Edge Source", actionOwnerName) + new ActionBuilder("Edge Source", ACTION_OWNER) .popupMenuPath("Go To Edge Source") .popupMenuGroup("Go To") .withContext(EdgeGraphActionContext.class) .onAction(c -> setFocusedVertex(graph.getEdgeSource(c.getClickedEdge()))) .buildAndInstallLocal(componentProvider); - new ActionBuilder("Edge Target", actionOwnerName) + new ActionBuilder("Edge Target", ACTION_OWNER) .popupMenuPath("Go To Edge Target") .popupMenuGroup("Go To") .withContext(EdgeGraphActionContext.class) .onAction(c -> setFocusedVertex(graph.getEdgeTarget(c.getClickedEdge()))) .buildAndInstallLocal(componentProvider); - hideSelectedAction = new ToggleActionBuilder("Hide Selected", actionOwnerName) + hideSelectedAction = new ToggleActionBuilder("Hide Selected", ACTION_OWNER) .popupMenuPath("Hide Selected") .popupMenuGroup("z", "1") .description("Toggles whether or not to show selected vertices and edges") .onAction(c -> manageVertexDisplay()) .buildAndInstallLocal(componentProvider); - hideUnselectedAction = new ToggleActionBuilder("Hide Unselected", actionOwnerName) + hideUnselectedAction = new ToggleActionBuilder("Hide Unselected", ACTION_OWNER) .popupMenuPath("Hide Unselected") .popupMenuGroup("z", "2") .description("Toggles whether or not to show selected vertices and edges") .onAction(c -> manageVertexDisplay()) .buildAndInstallLocal(componentProvider); - new ActionBuilder("Invert Selection", actionOwnerName) + new ActionBuilder("Invert Selection", ACTION_OWNER) .popupMenuPath("Invert Selection") .popupMenuGroup("z", "3") .description("Inverts the current selection") .onAction(c -> invertSelection()) .buildAndInstallLocal(componentProvider); - new ActionBuilder("Grow Selection To Targets", actionOwnerName) + new ActionBuilder("Grow Selection To Targets", ACTION_OWNER) .popupMenuPath("Grow Selection To Targets") .popupMenuGroup("z", "4") .description("Extends the current selection by including the target vertex " + @@ -442,7 +416,7 @@ public class DefaultGraphDisplay implements GraphDisplay { .onAction(c -> growSelection(getTargetVerticesFromSelected())) .buildAndInstallLocal(componentProvider); - new ActionBuilder("Grow Selection From Sources", actionOwnerName) + new ActionBuilder("Grow Selection From Sources", ACTION_OWNER) .popupMenuPath("Grow Selection From Sources") .popupMenuGroup("z", "4") .description("Extends the current selection by including the target vertex " + @@ -452,15 +426,23 @@ public class DefaultGraphDisplay implements GraphDisplay { .onAction(c -> growSelection(getSourceVerticesFromSelected())) .buildAndInstallLocal(componentProvider); - new ActionBuilder("Create Subgraph", actionOwnerName) - .popupMenuPath("Display Selected as New Graph") + new ActionBuilder("Clear Selection", ACTION_OWNER) + .popupMenuPath("Clear Selection") .popupMenuGroup("z", "5") + .keyBinding("escape") + .enabledWhen(c -> hasSelection()) + .onAction(c -> clearSelection()) + .buildAndInstallLocal(componentProvider); + + new ActionBuilder("Create Subgraph", ACTION_OWNER) + .popupMenuPath("Display Selected as New Graph") + .popupMenuGroup("zz", "5") .description("Creates a subgraph from the selected nodes") .enabledWhen(c -> !viewer.getSelectedVertexState().getSelected().isEmpty()) .onAction(c -> createAndDisplaySubGraph()) .buildAndInstallLocal(componentProvider); - togglePopupsAction = new ToggleActionBuilder("Display Popup Windows", actionOwnerName) + togglePopupsAction = new ToggleActionBuilder("Display Popup Windows", ACTION_OWNER) .popupMenuPath("Display Popup Windows") .popupMenuGroup("zz", "1") .description("Toggles whether or not to show popup windows, such as tool tips") @@ -471,6 +453,24 @@ public class DefaultGraphDisplay implements GraphDisplay { } + private void clearSelection() { + viewer.getSelectedVertexState().clear(); + viewer.getSelectedEdgeState().clear(); + } + + private boolean hasSelection() { + return !(viewer.getSelectedVertexState().getSelected().isEmpty() && + viewer.getSelectedEdgeState().getSelected().isEmpty()); + } + + private boolean isSelected(AttributedVertex v) { + return viewer.getSelectedVertexState().isSelected(v); + } + + private boolean isSelected(AttributedEdge e) { + return viewer.getSelectedEdgeState().isSelected(e); + } + private void createAndDisplaySubGraph() { GraphDisplay display = graphDisplayProvider.getGraphDisplay(false, TaskMonitor.DUMMY); try { @@ -556,7 +556,7 @@ public class DefaultGraphDisplay implements GraphDisplay { for (String layoutName : names) { ActionState state = new ActionState<>(layoutName, DefaultDisplayGraphIcons.LAYOUT_ALGORITHM_ICON, layoutName); - state.setHelpLocation(new HelpLocation(actionOwnerName, layoutName)); + state.setHelpLocation(new HelpLocation(ACTION_OWNER, layoutName)); actionStates.add(state); } return actionStates; @@ -603,24 +603,6 @@ public class DefaultGraphDisplay implements GraphDisplay { viewer.repaint(); } - private void displaySubGraph(Graph subGraph) { - - try { - GraphDisplay graphDisplay = - graphDisplayProvider.getGraphDisplay(false, TaskMonitor.DUMMY); - graphDisplay.setGraph((AttributedGraph) subGraph, "SubGraph", false, TaskMonitor.DUMMY); - graphDisplay.setGraphDisplayListener(listener); - } - catch (CancelledException e) { - // can't happen while using a dummy monitor - } - } - - /** - * create a SatelliteViewer for the Visualization - * @param parentViewer the main visualization 'parent' of the satellite view - * @return a new SatelliteVisualizationViewer - */ private SatelliteVisualizationViewer createSatelliteViewer( VisualizationViewer parentViewer) { Dimension viewerSize = parentViewer.getSize(); @@ -649,9 +631,6 @@ public class DefaultGraphDisplay implements GraphDisplay { return satellite; } - /** - * close this graph display - */ @Override public void close() { graphDisplayProvider.remove(this); @@ -662,10 +641,6 @@ public class DefaultGraphDisplay implements GraphDisplay { componentProvider.closeComponent(); } - /** - * accept a {@code GraphDisplayListener} - * @param listener the listener to be notified - */ @Override public void setGraphDisplayListener(GraphDisplayListener listener) { if (this.listener != null) { @@ -697,7 +672,7 @@ public class DefaultGraphDisplay implements GraphDisplay { viewer.getSelectedVertexState().addItemListener(switchableSelectionListener); } - protected void setFocusedVertex(AttributedVertex vertex) { + public void setFocusedVertex(AttributedVertex vertex) { setFocusedVertex(vertex, EventTrigger.API_CALL); } @@ -1008,8 +983,7 @@ public class DefaultGraphDisplay implements GraphDisplay { return new Point2D.Double(p.x, p.y); } - // they did not pick a vertex to center, so - // just center the graph + // they did not pick a vertex to center, so just center the graph Point2D center = viewer.getCenter(); Point p = Point.of(center.getX(), center.getY()); return new Point2D.Double(p.x, p.y); @@ -1211,13 +1185,13 @@ public class DefaultGraphDisplay implements GraphDisplay { public ActionContext getActionContext(MouseEvent e) { - AttributedVertex pickedVertex = graphMouse.getPickedVertex(e); + AttributedVertex pickedVertex = JgtUtils.getVertex(e, viewer); if (pickedVertex != null) { return new VertexGraphActionContext(componentProvider, graph, getSelectedVertices(), focusedVertex, pickedVertex); } - AttributedEdge pickedEdge = graphMouse.getPickedEdge(e); + AttributedEdge pickedEdge = JgtUtils.getEdge(e, viewer); if (pickedEdge != null) { return new EdgeGraphActionContext(componentProvider, graph, getSelectedVertices(), focusedVertex, pickedEdge); diff --git a/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/mouse/AbstractJgtGraphMousePlugin.java b/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/mouse/AbstractJgtGraphMousePlugin.java new file mode 100644 index 0000000000..d6bc5ea70d --- /dev/null +++ b/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/mouse/AbstractJgtGraphMousePlugin.java @@ -0,0 +1,311 @@ +/* ### + * 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.visualization.mouse; + +import java.awt.Cursor; +import java.awt.event.*; + +import org.jungrapht.visualization.SatelliteVisualizationViewer; +import org.jungrapht.visualization.VisualizationViewer; +import org.jungrapht.visualization.control.AbstractGraphMousePlugin; +import org.jungrapht.visualization.selection.MutableSelectedState; + +/** + * Graph mouse plugin base class. + * + * Usage Notes: + *
    + *
  • We clear state on mouseReleased() and mouseExited(), since we will get + * at least one of those calls
  • + *
+ * @param the vertex type + * @param the edge type + */ +//@formatter:off +public abstract class AbstractJgtGraphMousePlugin + extends AbstractGraphMousePlugin + implements MouseListener, MouseMotionListener { +//@formatter:on + + protected boolean isHandlingMouseEvents; + + protected V selectedVertex; + protected E selectedEdge; + + public AbstractJgtGraphMousePlugin() { + this(InputEvent.BUTTON1_DOWN_MASK); + } + + public AbstractJgtGraphMousePlugin(int selectionModifiers) { + super(selectionModifiers); + } + + public VisualizationViewer getViewer(MouseEvent e) { + VisualizationViewer viewer = getGraphViewer(e); + return viewer; + } + + /** + * Returns the primary/master graph viewer. + * + * @param e the mouse event from which to get the viewer + * @return the viewer + */ + @SuppressWarnings("unchecked") + public VisualizationViewer getGraphViewer(MouseEvent e) { + VisualizationViewer viewer = (VisualizationViewer) e.getSource(); + + // is this the satellite viewer? + if (viewer instanceof SatelliteVisualizationViewer) { + return ((SatelliteVisualizationViewer) viewer).getMaster(); + } + + return viewer; + } + + /** + * Returns the satellite graph viewer. This assumes that the mouse event originated from + * the satellite viewer. + * + * @param e the mouse event from which to get the viewer + * @return the viewer + */ + @SuppressWarnings("unchecked") + public SatelliteVisualizationViewer getSatelliteGraphViewer(MouseEvent e) { + + VisualizationViewer viewer = (VisualizationViewer) e.getSource(); + + // is this the satellite viewer? + if (viewer instanceof SatelliteVisualizationViewer) { + return (SatelliteVisualizationViewer) viewer; + } + + throw new IllegalStateException("Do not have a satellite GraphViewer"); + } + + /** + * Signals to perform any cleanup when this plugin is going away + */ + public void dispose() { + // stub + } + + /** + * Checks the given mouse event to see if it is a valid event for selecting a vertex at the + * mouse location. If so, then the vertex is selected in this mouse handler and the event + * is consumed. + * @param e the event + * @return true if a vertex was selected + */ + protected boolean checkForVertex(MouseEvent e) { + if (!checkModifiers(e)) { + selectedVertex = null; + return false; + } + + VisualizationViewer vv = getViewer(e); + selectedVertex = JgtUtils.getVertex(e, vv); + if (selectedVertex == null) { + return false; + } + + e.consume(); + return true; + } + + /** + * Checks the given mouse event to see if it is a valid event for selecting an edge at the + * mouse location. If so, then the edge is selected in this mouse handler and the event + * is consumed. + * @param e the event + * @return true if an edge was selected + */ + protected boolean checkForEdge(MouseEvent e) { + if (!checkModifiers(e) || isOverVertex(e)) { + selectedEdge = null; + return false; + } + + VisualizationViewer vv = getViewer(e); + selectedEdge = JgtUtils.getEdge(e, vv); + if (selectedEdge == null) { + return false; + } + + e.consume(); + isHandlingMouseEvents = true; + return true; + } + + /** + * Selects the given vertex + * @param vertex the vertex + * @param viewer the graph viewer + * @return true if the vertex is selected + */ + protected boolean selectVertex(V vertex, VisualizationViewer viewer) { + MutableSelectedState selectedVertexState = viewer.getSelectedVertexState(); + if (selectedVertexState == null) { + return false; + } + + selectedVertexState.isSelected(vertex); + + if (selectedVertexState.isSelected(vertex) == false) { + selectedVertexState.clear(); + selectedVertexState.select(vertex, true); + } + + return true; + } + + /** + * Selects the given edge + * @param edge the edge + * @param viewer the graph viewer + * @return true if the edge is selected + */ + protected boolean selectEdge(E edge, VisualizationViewer viewer) { + + MutableSelectedState selectedVertexState = viewer.getSelectedEdgeState(); + if (selectedVertexState == null) { + return false; + } + + selectedVertexState.isSelected(edge); + + if (selectedVertexState.isSelected(edge) == false) { + selectedVertexState.clear(); + selectedVertexState.select(edge, true); + } + return true; + } + + /** + * Returns true if the location of the mouse event is over a vertex + * @param e the event + * @return true if the location of the mouse event is over a vertex + */ + protected boolean isOverVertex(MouseEvent e) { + return getVertex(e) != null; + } + + /** + * Returns the vertex if the mouse event is over a vertex + * @param e the event + * @return a vertex or null + */ + protected V getVertex(MouseEvent e) { + VisualizationViewer viewer = getViewer(e); + return JgtUtils.getVertex(e, viewer); + } + + /** + * Returns true if the location of the mouse event is over a edge + * @param e the event + * @return true if the location of the mouse event is over a edge + */ + protected boolean isOverEdge(MouseEvent e) { + VisualizationViewer viewer = getViewer(e); + E edge = JgtUtils.getEdge(e, viewer); + if (edge == null) { + return false; + } + + return !isOverVertex(e); + } + + protected void installCursor(Cursor newCursor, MouseEvent e) { + VisualizationViewer viewer = getViewer(e); + viewer.setCursor(newCursor); + } + + protected boolean shouldShowCursor(MouseEvent e) { + return isOverVertex(e); // to showing cursor over vertices + } + + @Override + public void mousePressed(MouseEvent e) { + if (!checkModifiers(e)) { + return; + } + + // override this method to do stuff + } + + @Override + public void mouseClicked(MouseEvent e) { + if (!isHandlingMouseEvents) { + return; + } + + e.consume(); + resetState(); + } + + protected void resetState() { + isHandlingMouseEvents = false; + selectedVertex = null; + selectedEdge = null; + } + + @Override + public void mouseDragged(MouseEvent e) { + if (!isHandlingMouseEvents) { + return; + } + + e.consume(); + resetState(); + } + + @Override + public void mouseMoved(MouseEvent e) { + if (isHandlingMouseEvents) { + e.consume(); + } + + // only "turn on" the cursor; resetting is handled elsewhere (in the mouse driver) + if (shouldShowCursor(e)) { + installCursor(cursor, e); + e.consume(); + } + } + + @Override + public void mouseReleased(MouseEvent e) { + if (isHandlingMouseEvents) { + e.consume(); + } + + if (shouldShowCursor(e)) { + installCursor(cursor, e); + } + } + + @Override + public void mouseEntered(MouseEvent e) { + if (shouldShowCursor(e)) { + installCursor(cursor, e); + e.consume(); + } + } + + @Override + public void mouseExited(MouseEvent e) { + installCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR), e); + } +} diff --git a/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/mouse/JgtCursorRestoringPlugin.java b/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/mouse/JgtCursorRestoringPlugin.java new file mode 100644 index 0000000000..cb576b621f --- /dev/null +++ b/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/mouse/JgtCursorRestoringPlugin.java @@ -0,0 +1,55 @@ +/* ### + * 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.visualization.mouse; + +import java.awt.Cursor; +import java.awt.event.MouseEvent; +import java.awt.event.MouseMotionListener; + +import org.jungrapht.visualization.VisualizationViewer; +import org.jungrapht.visualization.control.AbstractGraphMousePlugin; + +/** + * Restores the cursor after other graph mouse operations. + * + * Future: this is copied from the Visual Graph counterpart--consolidate these + * + * @param the vertex type + * @param the edge type + */ +public class JgtCursorRestoringPlugin extends AbstractGraphMousePlugin + implements MouseMotionListener { + + public JgtCursorRestoringPlugin() { + super(0); + } + + @Override + public void mouseDragged(MouseEvent e) { + // don't care + } + + @Override + public void mouseMoved(MouseEvent e) { + installCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR), e); + } + + @SuppressWarnings("unchecked") + private void installCursor(Cursor newCursor, MouseEvent e) { + VisualizationViewer viewer = (VisualizationViewer) e.getSource(); + viewer.setCursor(newCursor); + } +} diff --git a/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/mouse/JgtEdgeNavigationPlugin.java b/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/mouse/JgtEdgeNavigationPlugin.java new file mode 100644 index 0000000000..f1514ce714 --- /dev/null +++ b/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/mouse/JgtEdgeNavigationPlugin.java @@ -0,0 +1,113 @@ +/* ### + * 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.visualization.mouse; + +import java.awt.Cursor; +import java.awt.event.MouseEvent; +import java.awt.geom.Point2D; + +import org.jgrapht.Graph; +import org.jungrapht.visualization.VisualizationViewer; +import org.jungrapht.visualization.layout.model.Point; +import org.jungrapht.visualization.selection.MutableSelectedState; + +import ghidra.graph.visualization.CenterAnimationJob; + +/** + * Mouse plugin to allow for edge navigation + * + * @param the vertex type + * @param the edge type + */ +public class JgtEdgeNavigationPlugin extends AbstractJgtGraphMousePlugin { + + public JgtEdgeNavigationPlugin() { + this.cursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR); + } + + @Override + public void mousePressed(MouseEvent e) { + if (!checkModifiers(e)) { + return; + } + + if (e.getClickCount() != 2) { + return; + } + + checkForEdge(e); // this will select an edge if we can and store off the edge + } + + @Override + public void mouseClicked(MouseEvent e) { + if (!isHandlingMouseEvents) { + return; + } + + E edge = selectedEdge; // save off before we reset + e.consume(); + resetState(); + + // on double-clicks we go to the vertex in the current edge direction unless that vertex + // is already selected, then we go to the other vertex + VisualizationViewer viewer = getViewer(e); + MutableSelectedState selectedState = viewer.getSelectedVertexState(); + + Graph graph = viewer.getVisualizationModel().getGraph(); + V end = graph.getEdgeTarget(edge); + if (!selectedState.isSelected(end)) { + pickAndShowVertex(end, selectedState, viewer); + return; + } + + // the destination was picked, go the other direction + V source = graph.getEdgeSource(edge); + pickAndShowVertex(source, selectedState, viewer); + } + + private void pickAndShowVertex(V vertex, MutableSelectedState selectedVertexState, + VisualizationViewer viewer) { + + // TODO animate; this requires a single view updater + Point2D existingCenter = viewer.getRenderContext() + .getMultiLayerTransformer() + .inverseTransform(viewer.getCenter()); + Point vp = viewer.getVisualizationModel().getLayoutModel().get(vertex); + Point2D newCenter = new Point2D.Double(vp.x, vp.y); + CenterAnimationJob job = new CenterAnimationJob(viewer, existingCenter, newCenter); + job.finished(); + + selectedVertexState.clear(); + selectedVertexState.select(vertex); + + /* + VisualGraphViewUpdater updater = viewer.getViewUpdater(); + updater.moveVertexToCenterWithAnimation(vertex, isBusy -> { + + // pick the vertex after the animation has run + if (!isBusy) { + GPickedState pickedStateWrapper = (GPickedState) selectedVertexState; + pickedStateWrapper.pickToActivate(vertex); + } + }); + */ + } + + @Override + protected boolean shouldShowCursor(MouseEvent e) { + return isOverEdge(e); + } +} diff --git a/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/mouse/JgtPluggableGraphMouse.java b/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/mouse/JgtPluggableGraphMouse.java new file mode 100644 index 0000000000..be575731c8 --- /dev/null +++ b/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/mouse/JgtPluggableGraphMouse.java @@ -0,0 +1,70 @@ +/* ### + * 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.visualization.mouse; + +import java.awt.event.InputEvent; + +import org.jungrapht.visualization.control.*; + +import docking.DockingUtils; +import ghidra.graph.visualization.DefaultGraphDisplay; +import ghidra.service.graph.AttributedEdge; +import ghidra.service.graph.AttributedVertex; + +/** + * Pluggable graph mouse for jungrapht + */ +public class JgtPluggableGraphMouse extends DefaultGraphMouse { + + private DefaultGraphDisplay graphDisplay; + + // TODO we should not need the graph display for any mouse plugins, but the API is net yet + // robust enough to communicate fully without it + public JgtPluggableGraphMouse(DefaultGraphDisplay graphDisplay) { + super(DefaultGraphMouse. builder()); + this.graphDisplay = graphDisplay; + } + + @Override + public void loadPlugins() { + + // + // Note: the order of these additions matters, as an event will flow to each plugin until + // it is handled. + // + + // edge + add(new JgtEdgeNavigationPlugin()); + + add(new JgtVertexFocusingPlugin(graphDisplay)); + + // scaling + add(new ScalingGraphMousePlugin(new CrossoverScalingControl(), 0, in, out)); + + // the grab/pan feature + add(new JgtTranslatingPlugin()); + + add(new SelectingGraphMousePlugin( + InputEvent.BUTTON1_DOWN_MASK, + 0, + DockingUtils.CONTROL_KEY_MODIFIER_MASK)); + + // cursor cleanup + add(new JgtCursorRestoringPlugin()); + + setPluginsLoaded(); + } +} diff --git a/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/mouse/JgtTranslatingPlugin.java b/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/mouse/JgtTranslatingPlugin.java new file mode 100644 index 0000000000..cf2ff54aff --- /dev/null +++ b/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/mouse/JgtTranslatingPlugin.java @@ -0,0 +1,177 @@ +/* ### + * 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.visualization.mouse; + +import java.awt.Cursor; +import java.awt.event.InputEvent; +import java.awt.event.MouseEvent; +import java.awt.geom.Point2D; + +import org.jungrapht.visualization.*; +import org.jungrapht.visualization.MultiLayerTransformer.Layer; +import org.jungrapht.visualization.control.TranslatingGraphMousePlugin; +import org.jungrapht.visualization.transform.MutableTransformer; + +/** + * Note: this class is based on {@link TranslatingGraphMousePlugin}. + *

+ * TranslatingGraphMousePlugin uses a MouseButtonOne press and drag gesture to translate + * the graph display in the x and y direction. The default MouseButtonOne modifier can be overridden + * to cause a different mouse gesture to translate the display. + * + * @param the vertex type + * @param the edge type + */ +public class JgtTranslatingPlugin + extends AbstractJgtGraphMousePlugin { + + private boolean panning; + private boolean isHandlingEvent; + + public JgtTranslatingPlugin() { + this(InputEvent.BUTTON1_DOWN_MASK); + } + + public JgtTranslatingPlugin(int modifiers) { + super(modifiers); + this.cursor = Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR); + } + + @Override + public void mousePressed(MouseEvent e) { + boolean accepted = checkModifiers(e) && isInDraggingArea(e); + if (!accepted) { + return; + } + + down = e.getPoint(); + VisualizationViewer viewer = getGraphViewer(e); + viewer.setCursor(cursor); + isHandlingEvent = true; + e.consume(); + } + + @Override + public void mouseReleased(MouseEvent e) { + boolean wasHandlingEvent = isHandlingEvent; + isHandlingEvent = false; + down = null; + installCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR), e); + + // NOTE: we are only consuming the event here if we actually did pan...this allows follow-on + // mouse handlers to process the mouseReleased() event. This is a bit odd and not the + // normal event processing (which is to consume all related events). + if (wasHandlingEvent && panning) { + e.consume(); + } + + panning = false; + } + + @Override + public void mouseDragged(MouseEvent e) { + boolean accepted = checkModifiers(e); + if (!accepted) { + return; + } + + if (!isHandlingEvent) { + return; + } + + panning = true; + + VisualizationViewer viewer = getGraphViewer(e); + RenderContext context = viewer.getRenderContext(); + MultiLayerTransformer multiLayerTransformer = context.getMultiLayerTransformer(); + MutableTransformer layoutTransformer = multiLayerTransformer.getTransformer(Layer.LAYOUT); + viewer.setCursor(cursor); + Point2D downPoint = multiLayerTransformer.inverseTransform(down); + Point2D p = multiLayerTransformer.inverseTransform(e.getPoint()); + float dx = (float) (p.getX() - downPoint.getX()); + float dy = (float) (p.getY() - downPoint.getY()); + + layoutTransformer.translate(dx, dy); + down.x = e.getX(); + down.y = e.getY(); + e.consume(); + viewer.repaint(); + } + + @Override + public void mouseClicked(MouseEvent e) { + // don't care + } + + @Override + public void mouseEntered(MouseEvent e) { + if (isHandlingEvent) { + return; + } + + if (!isInDraggingArea(e)) { + return; + } + + if (!checkModifiersForCursor(e)) { + return; + } + + installCursor(cursor, e); + } + + @Override + public void mouseExited(MouseEvent e) { + installCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR), e); + } + + @Override + public void mouseMoved(MouseEvent e) { + if (!checkModifiersForCursor(e)) { + return; + } + + if (isHandlingEvent) { + e.consume(); + } + + if (isInDraggingArea(e)) { + installCursor(cursor, e); + e.consume(); + } + } + + private boolean checkModifiersForCursor(MouseEvent e) { + if (e.getModifiersEx() == 0) { + return true; + } + return false; + } + +//================================================================================================== +// Private methods +//================================================================================================== + + private boolean isInDraggingArea(MouseEvent e) { + return !(isOverVertex(e) || isOverEdge(e)); + } + + @Override + public void installCursor(Cursor newCursor, MouseEvent e) { + VisualizationViewer viewer = getViewer(e); + viewer.setCursor(newCursor); + } +} diff --git a/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/GhidraGraphMouse.java b/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/mouse/JgtUtils.java similarity index 50% rename from Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/GhidraGraphMouse.java rename to Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/mouse/JgtUtils.java index 604fc853ea..33a15ac02a 100644 --- a/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/GhidraGraphMouse.java +++ b/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/mouse/JgtUtils.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.graph.visualization; +package ghidra.graph.visualization.mouse; import static org.jungrapht.visualization.VisualizationServer.*; @@ -22,42 +22,86 @@ import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import org.jungrapht.visualization.VisualizationViewer; -import org.jungrapht.visualization.control.*; +import org.jungrapht.visualization.control.GraphElementAccessor; +import org.jungrapht.visualization.control.TransformSupport; import org.jungrapht.visualization.layout.model.LayoutModel; import org.jungrapht.visualization.selection.ShapePickSupport; -import docking.ComponentProvider; -import ghidra.service.graph.AttributedEdge; -import ghidra.service.graph.AttributedVertex; - /** - * An extension of the jungrapht DefaultGraphMouse. This class has references to - *

    - *
  • a {@link VisualizationViewer} (to access the Graph and LayoutModel) - * + * Keeper of shared logic for jungrapht handling */ -public class GhidraGraphMouse extends DefaultGraphMouse { +public class JgtUtils { private static final String PICK_AREA_SIZE_PROPERTY = PREFIX + "pickAreaSize"; - private VisualizationViewer viewer; - - private int pickSize; - /** - * create an instance with default values - * @param componentProvider the graph component provider - * @param viewer the graph viewer component + * Returns the edge under the given mouse event + * + * @param the vertex type + * @param the edge type + * @param e the event + * @param viewer the graph viewer + * @return the edge */ - GhidraGraphMouse(ComponentProvider componentProvider, - VisualizationViewer viewer) { + public static E getEdge(MouseEvent e, VisualizationViewer viewer) { + if (e == null) { + return null; + } - super(DefaultGraphMouse. builder()); - this.viewer = viewer; - pickSize = Integer.getInteger(GhidraGraphMouse.PICK_AREA_SIZE_PROPERTY, 4); + Rectangle2D footprintRectangle = getFootprint(e); + LayoutModel layoutModel = viewer.getVisualizationModel().getLayoutModel(); + GraphElementAccessor pickSupport = viewer.getPickSupport(); + if (pickSupport == null) { + return null; + } + + if (pickSupport instanceof ShapePickSupport) { + ShapePickSupport shapePickSupport = + (ShapePickSupport) pickSupport; + return shapePickSupport.getEdge(layoutModel, footprintRectangle); + } + + TransformSupport transformSupport = + viewer.getTransformSupport(); + Point2D layoutPoint = transformSupport.inverseTransform(viewer, e.getPoint()); + return pickSupport.getEdge(layoutModel, layoutPoint.getX(), layoutPoint.getY()); } - private Rectangle2D getFootprint(MouseEvent e) { + /** + * Returns the vertex under the given mouse event + * + * @param the vertex type + * @param the edge type + * @param e the event + * @param viewer the graph viewer + * @return the vertex + */ + public static V getVertex(MouseEvent e, VisualizationViewer viewer) { + if (e == null) { + return null; + } + + Rectangle2D footprintRectangle = getFootprint(e); + LayoutModel layoutModel = viewer.getVisualizationModel().getLayoutModel(); + GraphElementAccessor pickSupport = viewer.getPickSupport(); + if (pickSupport == null) { + return null; + } + + if (pickSupport instanceof ShapePickSupport) { + ShapePickSupport shapePickSupport = + (ShapePickSupport) pickSupport; + return shapePickSupport.getVertex(layoutModel, footprintRectangle); + } + + TransformSupport transformSupport = + viewer.getTransformSupport(); + Point2D layoutPoint = transformSupport.inverseTransform(viewer, e.getPoint()); + return pickSupport.getVertex(layoutModel, layoutPoint.getX(), layoutPoint.getY()); + } + + private static Rectangle2D getFootprint(MouseEvent e) { + int pickSize = Integer.getInteger(PICK_AREA_SIZE_PROPERTY, 4); return new Rectangle2D.Float( e.getPoint().x - pickSize / 2f, e.getPoint().y - pickSize / 2f, @@ -65,44 +109,4 @@ public class GhidraGraphMouse extends DefaultGraphMouse layoutModel = viewer.getVisualizationModel().getLayoutModel(); - GraphElementAccessor pickSupport = - viewer.getPickSupport(); - if (pickSupport instanceof ShapePickSupport) { - ShapePickSupport shapePickSupport = - (ShapePickSupport) pickSupport; - return shapePickSupport.getEdge(layoutModel, footprintRectangle); - } - - TransformSupport transformSupport = - viewer.getTransformSupport(); - Point2D layoutPoint = transformSupport.inverseTransform(viewer, e.getPoint()); - return pickSupport.getEdge(layoutModel, layoutPoint.getX(), layoutPoint.getY()); - } - - AttributedVertex getPickedVertex(MouseEvent e) { - if (e == null) { - return null; - } - Rectangle2D footprintRectangle = getFootprint(e); - LayoutModel layoutModel = viewer.getVisualizationModel().getLayoutModel(); - GraphElementAccessor pickSupport = - viewer.getPickSupport(); - if (pickSupport instanceof ShapePickSupport) { - ShapePickSupport shapePickSupport = - (ShapePickSupport) pickSupport; - return shapePickSupport.getVertex(layoutModel, footprintRectangle); - } - - TransformSupport transformSupport = - viewer.getTransformSupport(); - Point2D layoutPoint = transformSupport.inverseTransform(viewer, e.getPoint()); - return pickSupport.getVertex(layoutModel, layoutPoint.getX(), layoutPoint.getY()); - } - } diff --git a/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/mouse/JgtVertexFocusingPlugin.java b/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/mouse/JgtVertexFocusingPlugin.java new file mode 100644 index 0000000000..cfac3f259c --- /dev/null +++ b/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/mouse/JgtVertexFocusingPlugin.java @@ -0,0 +1,57 @@ +/* ### + * 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.visualization.mouse; + +import java.awt.event.MouseEvent; + +import ghidra.graph.visualization.DefaultGraphDisplay; +import ghidra.service.graph.AttributedVertex; + +/** + * A mouse plugin to focus a vertex when clicked + * + * @param the vertex type + * @param the edge type + */ +public class JgtVertexFocusingPlugin extends AbstractJgtGraphMousePlugin { + + private DefaultGraphDisplay graphDisplay; + + public JgtVertexFocusingPlugin(DefaultGraphDisplay graphDisplay) { + this.graphDisplay = graphDisplay; + } + + @Override + public void mousePressed(MouseEvent e) { + if (!checkModifiers(e)) { + return; + } + + selectedVertex = getVertex(e); + } + + @Override + public void mouseClicked(MouseEvent e) { + if (selectedVertex == null) { + return; + } + + graphDisplay.setFocusedVertex((AttributedVertex) selectedVertex); + + // Note: do not consume the event. We will just focus our vertex, regardless of further + // mouse event processing. + } +} diff --git a/Ghidra/Features/GraphServices/src/main/resources/jungrapht.properties b/Ghidra/Features/GraphServices/src/main/resources/jungrapht.properties index 68092f7035..627ce97c18 100644 --- a/Ghidra/Features/GraphServices/src/main/resources/jungrapht.properties +++ b/Ghidra/Features/GraphServices/src/main/resources/jungrapht.properties @@ -62,3 +62,10 @@ jungrapht.initialDimensionVertexDensity=0.3f jungrapht.minScale=0.001 jungrapht.maxScale=1.0 + + +11:45 AM + +# not using spatial data structures for edges (fixed in versions after 1.0) +jungrapht.edgeSpatialSupport=NONE +jungrapht.vertexSpatialSupport=NONE diff --git a/Ghidra/Features/ProgramGraph/src/main/help/help/topics/ProgramGraphPlugin/ProgramGraph.htm b/Ghidra/Features/ProgramGraph/src/main/help/help/topics/ProgramGraphPlugin/ProgramGraph.htm index d23c6eac22..0cb44ad2d6 100644 --- a/Ghidra/Features/ProgramGraph/src/main/help/help/topics/ProgramGraphPlugin/ProgramGraph.htm +++ b/Ghidra/Features/ProgramGraph/src/main/help/help/topics/ProgramGraphPlugin/ProgramGraph.htm @@ -13,7 +13,7 @@

    Graphing the Program

    -

    Graph Output

    +

    Graph Output

    To display or export a graph, Ghidra supports multiple graph services. Ghidra has two built-in graph services; one to display a graph and one to export a graph. Before invoking @@ -21,7 +21,7 @@ will direct the output of the graph function to the active graph service. To select a graph service, use the GraphGraph Output

  • menu. -

    Graph types

    +

    Graph types

    Program control flow Graphs can be created and then shown using an appropriate graph service. A control flow graph is a representation of the flow from one portion of the code to another. The nodes of the graph represent blocks of code and the edges represent flow between @@ -49,7 +49,7 @@

    Selection and Location events are synchronized between each graph and the other windows in the tool. -

    Selection

    +

    Selection

    The current selection within the graph display is represented by a red box around selected nodes as shown below on the node labeled "00408133". A node is selected if any addresses it represents are contained within the @@ -74,7 +74,7 @@ from the basic blocks found within the selected subroutine.

-

Location

+

Location

The node containing the current address location is marked with a large red arrow as shown below on the graph node labeled "00408133".

@@ -95,7 +95,7 @@ current address location within Ghidra to change to the minimum address represented by the graph node.

-

Graph Representation

+

Graph Representation

By Default, the graphs use the following icons and colors to represent the nodes and edges.

@@ -220,99 +220,7 @@ -

Block Flow Graph

- -

A Block Flow Graph consists of nodes that represent Basic blocks of contiguous instructions. - Basic blocks are broken up by any instruction that causes a change in execution flow. All Jump, - Call, Branch, and Return instructions can cause the execution flow to change. Arithmetic and - store/load instructions do not break a Basic block because they do not change the execution - flow. A labeled instruction will always start a block regardless of the instruction type.

-

For example:

- -

- -

Would generate the following graph:

- -

-

- -
-

If there is a current selection, the nodes and edges - will be restricted to blocks of code that fall within the selection.

-
- -

To Graph Block Flow Using the default model,

- -
    -
  1. Select Graph Block Flow
  2. - -
  3. A new graph window is created
  4. -
- -

Graph Code Flow

- -

A Code Flow Graph is an extension of a Block Flow - Graph in which each graph node (i.e., vertex) contains the list of instructions contained - within the associated block. The list of instructions are passed to the graph as the vertex - label.

- -


-
-

- -

Graph Calls

- -

A graph of the call instruction flow from one subroutine to another can be created with - Graph Calls. The graph is created using the default - Call Model. Several Subroutine Models are available. Each model provides a slightly - different perspective on what constitutes a subroutine.

- -
-
-

If there is a current selection, the nodes and edges - will be restricted to blocks of code that fall within the selection.

-
- -

To Graph Calls Using the default model,

- -
    -
  1. Select Graph Calls
  2. - -
  3. A new graph window is created
  4. -
- -

To Graph Calls Using a specific model*,

- -
    -
  1. Select Graph Calls Using Model <a Call Model>
  2. - -
  3. - Select one of - -
      -
    • Isolated Entry Model
    • - -
    • Multiple Entry Model
    • - -
    • Overlapped Code Model
    • - -
    • Partitioned Code Model
    • -
    -
  4. - -
  5. A new graph window is created
  6. -
- -
-

*For a more thorough description of each Call Block - Model (i.e., Subroutine Model), see Block - Models. The specific list of models presented to the user may vary depending upon the - set of block models configured into the tool.

-
-
-

Reuse Graph

When Reuse Graph is turned on, creating any new graphs will re-use the active graph @@ -378,6 +286,123 @@

When Show Location is turned off, the graph view will not change as the current address location changes.

+ + +
+
+
+
+
+
+
+
+
+
+
+ +

Block Flow Graph

+ +

A Block Flow Graph consists of nodes that represent Basic blocks of contiguous instructions. + Basic blocks are broken up by any instruction that causes a change in execution flow. All Jump, + Call, Branch, and Return instructions can cause the execution flow to change. Arithmetic and + store/load instructions do not break a Basic block because they do not change the execution + flow. A labeled instruction will always start a block regardless of the instruction type.

+ +

For example:

+ +

+ +

Would generate the following graph:

+ +

+

+ +
+

If there is a current selection, the nodes and edges + will be restricted to blocks of code that fall within the selection.

+
+ +

To Graph Block Flow Using the default model,

+ +
    +
  1. Select Graph Block Flow
  2. + +
  3. A new graph window is created
  4. +
+ +
+ +

Rename Vertex

+
+

Allows the user to rename the symbol represented by the given vertex. +

+
+ +
+ + +

Graph Code Flow

+ +

A Code Flow Graph is an extension of a Block Flow + Graph in which each graph node (i.e., vertex) contains the list of instructions contained + within the associated block. The list of instructions are passed to the graph as the vertex + label.

+ +


+
+

+ +

Graph Calls

+ +

A graph of the call instruction flow from one subroutine to another can be created with + Graph Calls. The graph is created using the default + Call Model. Several Subroutine Models are available. Each model provides a slightly + different perspective on what constitutes a subroutine.

+ +
+
+

If there is a current selection, the nodes and edges + will be restricted to blocks of code that fall within the selection.

+
+ +

To Graph Calls Using the default model,

+ +
    +
  1. Select Graph Calls
  2. + +
  3. A new graph window is created
  4. +
+ +

To Graph Calls Using a specific model*,

+ +
    +
  1. Select Graph Calls Using Model <a Call Model>
  2. + +
  3. + Select one of + +
      +
    • Isolated Entry Model
    • + +
    • Multiple Entry Model
    • + +
    • Overlapped Code Model
    • + +
    • Partitioned Code Model
    • +
    +
  4. + +
  5. A new graph window is created
  6. +
+ +
+

*For a more thorough description of each Call Block + Model (i.e., Subroutine Model), see Block + Models. The specific list of models presented to the user may vary depending upon the + set of block models configured into the tool.

+
+

diff --git a/Ghidra/Features/ProgramGraph/src/main/java/ghidra/graph/program/BlockModelGraphDisplayListener.java b/Ghidra/Features/ProgramGraph/src/main/java/ghidra/graph/program/BlockModelGraphDisplayListener.java index 858fc4a804..7a03962fca 100644 --- a/Ghidra/Features/ProgramGraph/src/main/java/ghidra/graph/program/BlockModelGraphDisplayListener.java +++ b/Ghidra/Features/ProgramGraph/src/main/java/ghidra/graph/program/BlockModelGraphDisplayListener.java @@ -26,6 +26,7 @@ import ghidra.program.model.block.*; import ghidra.program.model.symbol.Symbol; import ghidra.program.model.symbol.SymbolTable; import ghidra.service.graph.*; +import ghidra.util.HelpLocation; import ghidra.util.exception.CancelledException; import ghidra.util.task.TaskMonitor; @@ -47,6 +48,7 @@ public class BlockModelGraphDisplayListener extends AddressBasedGraphDisplayList display.addAction(new ActionBuilder("Rename Vertex", "Block Graph") .popupMenuPath("Rename Vertex") .withContext(VertexGraphActionContext.class) + .helpLocation(new HelpLocation("ProgramGraphPlugin", "Rename Vertex")) // only enable action when vertex corresponds to an address .enabledWhen(c -> getAddress(c.getClickedVertex().getId()) != null) .onAction(this::updateVertexName) diff --git a/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/GraphComponent.java b/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/GraphComponent.java index f1141d40ae..f7d4c526e6 100644 --- a/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/GraphComponent.java +++ b/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/GraphComponent.java @@ -1128,9 +1128,13 @@ public class GraphComponent, G e private V selectedVertex; - @SuppressWarnings("deprecation") // deprecated until we fix the checkModifiers() code public VertexClickMousePlugin() { - super(InputEvent.BUTTON1_MASK); + super(InputEvent.BUTTON1_DOWN_MASK); + } + + @Override + public boolean checkModifiers(MouseEvent e) { + return e.getModifiersEx() == modifiers; } @Override @@ -1203,6 +1207,5 @@ public class GraphComponent, G e public void mouseExited(MouseEvent e) { // stub } - } } diff --git a/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/event/mouse/VisualGraphAbstractGraphMousePlugin.java b/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/event/mouse/VisualGraphAbstractGraphMousePlugin.java index 4ac46b1d3b..2e256c4042 100644 --- a/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/event/mouse/VisualGraphAbstractGraphMousePlugin.java +++ b/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/event/mouse/VisualGraphAbstractGraphMousePlugin.java @@ -32,6 +32,9 @@ import ghidra.graph.viewer.*; *
  • We clear state on mouseReleased() and mouseExited(), since we will get * at least one of those calls
  • * + * + * @param the vertex type + * @param the edge type */ //@formatter:off public abstract class VisualGraphAbstractGraphMousePlugin the vertex type + * @param the edge type */ public class VisualGraphAnimatedPickingGraphMousePlugin> extends AnimatedPickingGraphMousePlugin implements VisualGraphMousePlugin { @@ -32,7 +35,7 @@ public class VisualGraphAnimatedPickingGraphMousePlugin extends AbstractGr super(0); } + @Override + public boolean checkModifiers(MouseEvent e) { + return e.getModifiersEx() == modifiers; + } + + @Override public void mouseDragged(MouseEvent e) { // don't care } + @Override public void mouseMoved(MouseEvent e) { installCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR), e); } diff --git a/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/event/mouse/VisualGraphEventForwardingGraphMousePlugin.java b/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/event/mouse/VisualGraphEventForwardingGraphMousePlugin.java index a280b79851..e19320608f 100644 --- a/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/event/mouse/VisualGraphEventForwardingGraphMousePlugin.java +++ b/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/event/mouse/VisualGraphEventForwardingGraphMousePlugin.java @@ -38,9 +38,9 @@ public class VisualGraphEventForwardingGraphMousePlugin viewer) { - super(InputEvent.BUTTON1_MASK | InputEvent.BUTTON2_MASK | InputEvent.BUTTON3_MASK); + super(InputEvent.BUTTON1_DOWN_MASK | InputEvent.BUTTON2_DOWN_MASK | + InputEvent.BUTTON3_DOWN_MASK); this.viewer = Objects.requireNonNull(viewer); viewer.addPostRenderPaintable(paintable); } @Override public boolean checkModifiers(MouseEvent e) { - int eventModifiers = e.getModifiers(); + int eventModifiers = e.getModifiersEx(); eventModifiers = turnOffControlKey(eventModifiers); return ((eventModifiers & getModifiers()) == eventModifiers); } diff --git a/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/event/mouse/VisualGraphPickingGraphMousePlugin.java b/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/event/mouse/VisualGraphPickingGraphMousePlugin.java index 51c33b3fa0..5bd2779934 100644 --- a/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/event/mouse/VisualGraphPickingGraphMousePlugin.java +++ b/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/event/mouse/VisualGraphPickingGraphMousePlugin.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -39,18 +38,17 @@ public class VisualGraphPickingGraphMousePlugin { public VisualGraphSatelliteAbstractGraphMousePlugin() { - this(InputEvent.BUTTON1_MASK); + this(InputEvent.BUTTON1_DOWN_MASK); } public VisualGraphSatelliteAbstractGraphMousePlugin(int selectionModifiers) { diff --git a/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/event/mouse/VisualGraphSatelliteAnimatedPickingGraphMousePlugin.java b/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/event/mouse/VisualGraphSatelliteAnimatedPickingGraphMousePlugin.java deleted file mode 100644 index 26dec6c33a..0000000000 --- a/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/event/mouse/VisualGraphSatelliteAnimatedPickingGraphMousePlugin.java +++ /dev/null @@ -1,25 +0,0 @@ -/* ### - * 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.event.mouse; - -import ghidra.graph.viewer.VisualEdge; -import ghidra.graph.viewer.VisualVertex; - -public class VisualGraphSatelliteAnimatedPickingGraphMousePlugin> - extends VisualGraphAnimatedPickingGraphMousePlugin { - - // TODO - delete this class--it should not longer be needed -} diff --git a/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/event/mouse/VisualGraphSatelliteEdgeSelectionGraphMousePlugin.java b/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/event/mouse/VisualGraphSatelliteEdgeSelectionGraphMousePlugin.java deleted file mode 100644 index 1b9f456a90..0000000000 --- a/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/event/mouse/VisualGraphSatelliteEdgeSelectionGraphMousePlugin.java +++ /dev/null @@ -1,25 +0,0 @@ -/* ### - * 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.event.mouse; - -import ghidra.graph.viewer.VisualEdge; -import ghidra.graph.viewer.VisualVertex; - -public class VisualGraphSatelliteEdgeSelectionGraphMousePlugin> - extends VisualGraphEdgeSelectionGraphMousePlugin { - - // TODO this class probably can be deleted now -} diff --git a/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/event/mouse/VisualGraphSatelliteNavigationGraphMousePlugin.java b/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/event/mouse/VisualGraphSatelliteNavigationGraphMousePlugin.java index 22bf89e201..ba90a894ff 100644 --- a/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/event/mouse/VisualGraphSatelliteNavigationGraphMousePlugin.java +++ b/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/event/mouse/VisualGraphSatelliteNavigationGraphMousePlugin.java @@ -25,7 +25,7 @@ public class VisualGraphSatelliteNavigationGraphMousePlugin { public VisualGraphSatelliteNavigationGraphMousePlugin() { - super(InputEvent.BUTTON1_MASK); + super(InputEvent.BUTTON1_DOWN_MASK); } @Override diff --git a/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/event/mouse/VisualGraphSatelliteScalingGraphMousePlugin.java b/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/event/mouse/VisualGraphSatelliteScalingGraphMousePlugin.java index 71756c346d..f84deae84b 100644 --- a/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/event/mouse/VisualGraphSatelliteScalingGraphMousePlugin.java +++ b/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/event/mouse/VisualGraphSatelliteScalingGraphMousePlugin.java @@ -46,7 +46,7 @@ public class VisualGraphSatelliteScalingGraphMousePlugin the vertex type + * @param the edge type */ public class VisualGraphScalingGraphMousePlugin> extends ScalingGraphMousePlugin implements VisualGraphMousePlugin { @@ -51,8 +54,8 @@ public class VisualGraphScalingGraphMousePlugin the vertex type + * @param the edge type */ public class VisualGraphTranslatingGraphMousePlugin> extends AbstractGraphMousePlugin @@ -43,9 +43,8 @@ public class VisualGraphTranslatingGraphMousePlugin viewer = getGraphViewer(e); @@ -158,7 +162,7 @@ public class VisualGraphTranslatingGraphMousePlugin the vertex type + * @param the edge type */ public class VisualGraphZoomingPickingGraphMousePlugin> extends VisualGraphAbstractGraphMousePlugin { - // TODO for deprecated usage note, see the VisualGraphMousePlugin interface public VisualGraphZoomingPickingGraphMousePlugin() { - super(InputEvent.BUTTON1_MASK); + super(InputEvent.BUTTON1_DOWN_MASK); this.cursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR); } diff --git a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/GraphServicesScreenShots.java b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/GraphServicesScreenShots.java index c3ca2e97eb..c776dc3f94 100644 --- a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/GraphServicesScreenShots.java +++ b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/GraphServicesScreenShots.java @@ -72,10 +72,11 @@ public class GraphServicesScreenShots extends GhidraScreenShotGenerator { AttributedEdge e2 = graph.addEdge(v1, v3); e2.setAttribute("EdgeType", "Unconditional-Call"); - display.setGraph(graph, "test", false, TaskMonitor.DUMMY); + display.setGraph(graph, "Program Graph", false, TaskMonitor.DUMMY); waitForSwing(); setGraphWindowSize(700, 500); - ((DefaultGraphDisplay) display).centerAndScale(); + runSwing(() -> ((DefaultGraphDisplay) display).centerAndScale()); + waitForSwing(); captureProvider(DefaultGraphDisplayComponentProvider.class); } @@ -95,7 +96,7 @@ public class GraphServicesScreenShots extends GhidraScreenShotGenerator { provider.getComponent().requestFocus(); paintFix(window); }); - + waitForSwing(); } }