mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 02:39:44 +02:00
GP-3402 - Updated the Graph service API to better manage concurrent
threaded accesses
This commit is contained in:
parent
de55e42686
commit
bde74ad4d3
12 changed files with 422 additions and 387 deletions
|
@ -56,11 +56,6 @@ public abstract class AddressBasedGraphDisplayListener
|
||||||
program.addListener(this);
|
program.addListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void graphClosed() {
|
|
||||||
dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void locationFocusChanged(AttributedVertex vertex) {
|
public void locationFocusChanged(AttributedVertex vertex) {
|
||||||
Address address = getAddress(vertex);
|
Address address = getAddress(vertex);
|
||||||
|
|
|
@ -26,10 +26,10 @@ import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ghidra service interface for managing and directing graph output. It purpose is to discover
|
* Ghidra service interface for managing and directing graph output. It purpose is to discover
|
||||||
* available graphing display providers and (if more than one) allow the user to select the currently
|
* available graphing display providers and (if more than one) allow the user to select the
|
||||||
* active graph consumer. Clients that generate graphs don't have to worry about how to display them
|
* currently active graph consumer. Clients that generate graphs don't have to worry about how to
|
||||||
* or export graphs. They simply send their graphs to the broker and register for graph events if
|
* display them or export graphs. They simply send their graphs to the broker and register for graph
|
||||||
* they want interactive support.
|
* events if they want interactive support.
|
||||||
*/
|
*/
|
||||||
@ServiceInfo(defaultProvider = GraphDisplayBrokerPlugin.class, description = "Get a Graph Display")
|
@ServiceInfo(defaultProvider = GraphDisplayBrokerPlugin.class, description = "Get a Graph Display")
|
||||||
public interface GraphDisplayBroker {
|
public interface GraphDisplayBroker {
|
||||||
|
@ -56,7 +56,7 @@ public interface GraphDisplayBroker {
|
||||||
/**
|
/**
|
||||||
* A convenience method for getting a {@link GraphDisplay} from the currently active provider.
|
* A convenience method for getting a {@link GraphDisplay} from the currently active provider.
|
||||||
* This method is intended to be used to display a new graph.
|
* This method is intended to be used to display a new graph.
|
||||||
*
|
*
|
||||||
* @param reuseGraph if true, the provider will attempt to re-use a current graph display
|
* @param reuseGraph if true, the provider will attempt to re-use a current graph display
|
||||||
* @param monitor the {@link TaskMonitor} that can be used to cancel the operation
|
* @param monitor the {@link TaskMonitor} that can be used to cancel the operation
|
||||||
* @return a {@link GraphDisplay} object to sends graphs to be displayed or exported.
|
* @return a {@link GraphDisplay} object to sends graphs to be displayed or exported.
|
||||||
|
@ -87,7 +87,7 @@ public interface GraphDisplayBroker {
|
||||||
/**
|
/**
|
||||||
* Returns the {@link AttributedGraphExporter} with the given name or null in no exporter with
|
* Returns the {@link AttributedGraphExporter} with the given name or null in no exporter with
|
||||||
* that name is known
|
* that name is known
|
||||||
*
|
*
|
||||||
* @param name the name of the exporter to retrieve
|
* @param name the name of the exporter to retrieve
|
||||||
* @return the {@link AttributedGraphExporter} with the given name or null if no exporter with
|
* @return the {@link AttributedGraphExporter} with the given name or null if no exporter with
|
||||||
* that name is known
|
* that name is known
|
||||||
|
|
|
@ -17,23 +17,22 @@ package ghidra.graph.export;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import org.jgrapht.Graph;
|
|
||||||
|
|
||||||
import docking.action.DockingActionIf;
|
import docking.action.DockingActionIf;
|
||||||
import docking.widgets.EventTrigger;
|
import docking.widgets.EventTrigger;
|
||||||
import ghidra.app.services.GraphDisplayBroker;
|
import ghidra.app.services.GraphDisplayBroker;
|
||||||
import ghidra.framework.plugintool.PluginTool;
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
import ghidra.service.graph.*;
|
import ghidra.service.graph.*;
|
||||||
|
import ghidra.util.Swing;
|
||||||
import ghidra.util.exception.CancelledException;
|
import ghidra.util.exception.CancelledException;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link GraphDisplay} implementation for exporting graphs. In this case, there is no
|
* {@link GraphDisplay} implementation for exporting graphs. In this case, there is no
|
||||||
* associated visual display, instead the graph output gets sent to a file. The
|
* associated visual display, instead the graph output gets sent to a file. The
|
||||||
* {@link GraphDisplay} is mostly just a placeholder for executing the export function. By
|
* {@link GraphDisplay} is mostly just a placeholder for executing the export function. By
|
||||||
* hijacking the {@link GraphDisplayProvider} and {@link GraphDisplay} interfaces for exporting,
|
* hijacking the {@link GraphDisplayProvider} and {@link GraphDisplay} interfaces for exporting,
|
||||||
* all graph generating operations can be exported instead of being displayed without changing
|
* all graph generating operations can be exported instead of being displayed without changing
|
||||||
* the graph generation code.
|
* the graph generation code.
|
||||||
*/
|
*/
|
||||||
class ExportAttributedGraphDisplay implements GraphDisplay {
|
class ExportAttributedGraphDisplay implements GraphDisplay {
|
||||||
|
|
||||||
|
@ -66,7 +65,8 @@ class ExportAttributedGraphDisplay implements GraphDisplay {
|
||||||
*/
|
*/
|
||||||
private void doSetGraphData(AttributedGraph attributedGraph) {
|
private void doSetGraphData(AttributedGraph attributedGraph) {
|
||||||
List<AttributedGraphExporter> exporters = findGraphExporters();
|
List<AttributedGraphExporter> exporters = findGraphExporters();
|
||||||
GraphExporterDialog dialog = new GraphExporterDialog(attributedGraph, exporters);
|
GraphExporterDialog dialog =
|
||||||
|
Swing.runNow(() -> new GraphExporterDialog(attributedGraph, exporters));
|
||||||
tool.showDialog(dialog);
|
tool.showDialog(dialog);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,8 +79,7 @@ class ExportAttributedGraphDisplay implements GraphDisplay {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setGraph(AttributedGraph graph, String title, boolean append,
|
public void setGraph(AttributedGraph graph, String title, boolean append, TaskMonitor monitor) {
|
||||||
TaskMonitor monitor) {
|
|
||||||
this.title = title;
|
this.title = title;
|
||||||
this.graph = graph;
|
this.graph = graph;
|
||||||
doSetGraphData(graph);
|
doSetGraphData(graph);
|
||||||
|
@ -92,9 +91,6 @@ class ExportAttributedGraphDisplay implements GraphDisplay {
|
||||||
this.setGraph(graph, title, append, monitor);
|
this.setGraph(graph, title, append, monitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* remove all vertices and edges from the {@link Graph}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void clear() {
|
public void clear() {
|
||||||
// not interactive, so N/A
|
// not interactive, so N/A
|
||||||
|
|
|
@ -207,7 +207,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
|
|
||||||
viewer.setInitialDimensionFunction(
|
viewer.setInitialDimensionFunction(
|
||||||
InitialDimensionFunction.builder(viewer.getRenderContext().getVertexBoundsFunction())
|
InitialDimensionFunction.builder(viewer.getRenderContext().getVertexBoundsFunction())
|
||||||
.build());
|
.build());
|
||||||
createToolbarActions();
|
createToolbarActions();
|
||||||
createPopupActions();
|
createPopupActions();
|
||||||
connectSelectionStateListeners();
|
connectSelectionStateListeners();
|
||||||
|
@ -255,19 +255,18 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
viewer.getRenderContext().getMultiLayerTransformer().getTransformer(VIEW);
|
viewer.getRenderContext().getMultiLayerTransformer().getTransformer(VIEW);
|
||||||
|
|
||||||
MagnifyShapeTransformer shapeTransformer = MagnifyShapeTransformer.builder(lens)
|
MagnifyShapeTransformer shapeTransformer = MagnifyShapeTransformer.builder(lens)
|
||||||
// this lens' delegate is the viewer's VIEW layer, abandoned above
|
// this lens' delegate is the viewer's VIEW layer, abandoned above
|
||||||
.delegate(transformer)
|
.delegate(transformer)
|
||||||
.build();
|
.build();
|
||||||
LensGraphMouse lensGraphMouse =
|
LensGraphMouse lensGraphMouse = DefaultLensGraphMouse.builder()
|
||||||
DefaultLensGraphMouse.builder()
|
|
||||||
.magnificationFloor(1.f)
|
.magnificationFloor(1.f)
|
||||||
.magnificationCeiling(60.f)
|
.magnificationCeiling(60.f)
|
||||||
.magnificationDelta(.2f)
|
.magnificationDelta(.2f)
|
||||||
.build();
|
.build();
|
||||||
return MagnifyImageLensSupport.builder(viewer)
|
return MagnifyImageLensSupport.builder(viewer)
|
||||||
.lensTransformer(shapeTransformer)
|
.lensTransformer(shapeTransformer)
|
||||||
.lensGraphMouse(lensGraphMouse)
|
.lensGraphMouse(lensGraphMouse)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void buildHighlighers() {
|
private void buildHighlighers() {
|
||||||
|
@ -278,20 +277,20 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
|
|
||||||
// for highlighting of multiple selected vertices
|
// for highlighting of multiple selected vertices
|
||||||
this.multiSelectedVertexPaintable = MultiSelectedVertexPaintable.builder(viewer)
|
this.multiSelectedVertexPaintable = MultiSelectedVertexPaintable.builder(viewer)
|
||||||
.selectionStrokeMin(15.f)
|
.selectionStrokeMin(15.f)
|
||||||
.selectionPaint(getSelectedVertexColor())
|
.selectionPaint(getSelectedVertexColor())
|
||||||
.useBounds(true)
|
.useBounds(true)
|
||||||
.useOval(true)
|
.useOval(true)
|
||||||
.highlightScale(1.15)
|
.highlightScale(1.15)
|
||||||
.fillHighlight(false)
|
.fillHighlight(false)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
// manages highlight painting of a single selected vertex
|
// manages highlight painting of a single selected vertex
|
||||||
this.singleSelectedVertexPaintable = SingleSelectedVertexPaintable.builder(viewer)
|
this.singleSelectedVertexPaintable = SingleSelectedVertexPaintable.builder(viewer)
|
||||||
.selectionStrokeMin(4.f)
|
.selectionStrokeMin(4.f)
|
||||||
.selectionPaint(getSelectedVertexColor())
|
.selectionPaint(getSelectedVertexColor())
|
||||||
.selectedVertexFunction(vs -> this.focusedVertex)
|
.selectedVertexFunction(vs -> this.focusedVertex)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
// draws the selection highlights
|
// draws the selection highlights
|
||||||
viewer.addPreRenderPaintable(multiSelectedVertexPaintable);
|
viewer.addPreRenderPaintable(multiSelectedVertexPaintable);
|
||||||
|
@ -302,9 +301,9 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
viewer.removePreRenderPaintable(selectedEdgePaintable);
|
viewer.removePreRenderPaintable(selectedEdgePaintable);
|
||||||
|
|
||||||
this.selectedEdgePaintable = SelectedEdgePaintable.builder(viewer)
|
this.selectedEdgePaintable = SelectedEdgePaintable.builder(viewer)
|
||||||
.selectionPaintFunction(e -> getSelectedEdgeColor())
|
.selectionPaintFunction(e -> getSelectedEdgeColor())
|
||||||
.selectionStrokeMultiplier(2)
|
.selectionStrokeMultiplier(2)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
viewer.addPreRenderPaintable(selectedEdgePaintable);
|
viewer.addPreRenderPaintable(selectedEdgePaintable);
|
||||||
|
|
||||||
|
@ -317,209 +316,209 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
|
|
||||||
// create a toggle for 'scroll to selected vertex'
|
// create a toggle for 'scroll to selected vertex'
|
||||||
new ToggleActionBuilder("Scroll To Selection", ACTION_OWNER)
|
new ToggleActionBuilder("Scroll To Selection", ACTION_OWNER)
|
||||||
.toolBarIcon(Icons.NAVIGATE_ON_INCOMING_EVENT_ICON)
|
.toolBarIcon(Icons.NAVIGATE_ON_INCOMING_EVENT_ICON)
|
||||||
.description("Ensure that the 'focused' vertex is visible")
|
.description("Ensure that the 'focused' vertex is visible")
|
||||||
.selected(true)
|
.selected(true)
|
||||||
.onAction(context -> ensureVertexIsVisible =
|
.onAction(context -> ensureVertexIsVisible =
|
||||||
((AbstractButton) context.getSourceObject()).isSelected())
|
((AbstractButton) context.getSourceObject()).isSelected())
|
||||||
.buildAndInstallLocal(componentProvider);
|
.buildAndInstallLocal(componentProvider);
|
||||||
|
|
||||||
this.ensureVertexIsVisible = true; // since we initialized action to selected
|
this.ensureVertexIsVisible = true; // since we initialized action to selected
|
||||||
|
|
||||||
// create a toggle for enabling 'free-form' selection: selection is inside of a traced
|
// create a toggle for enabling 'free-form' selection: selection is inside of a traced
|
||||||
// shape instead of a rectangle
|
// shape instead of a rectangle
|
||||||
new ToggleActionBuilder("Free-Form Selection", ACTION_OWNER)
|
new ToggleActionBuilder("Free-Form Selection", ACTION_OWNER)
|
||||||
.toolBarIcon(DefaultDisplayGraphIcons.LASSO_ICON)
|
.toolBarIcon(DefaultDisplayGraphIcons.LASSO_ICON)
|
||||||
.description("Trace Free-Form Shape to select multiple vertices (CTRL-click-drag)")
|
.description("Trace Free-Form Shape to select multiple vertices (CTRL-click-drag)")
|
||||||
.selected(false)
|
.selected(false)
|
||||||
.onAction(context -> freeFormSelection =
|
.onAction(context -> freeFormSelection =
|
||||||
((AbstractButton) context.getSourceObject()).isSelected())
|
((AbstractButton) context.getSourceObject()).isSelected())
|
||||||
.buildAndInstallLocal(componentProvider);
|
.buildAndInstallLocal(componentProvider);
|
||||||
|
|
||||||
// create an icon button to display the satellite view
|
// create an icon button to display the satellite view
|
||||||
new ToggleActionBuilder("SatelliteView", ACTION_OWNER).description("Show Satellite View")
|
new ToggleActionBuilder("SatelliteView", ACTION_OWNER).description("Show Satellite View")
|
||||||
.toolBarIcon(DefaultDisplayGraphIcons.SATELLITE_VIEW_ICON)
|
.toolBarIcon(DefaultDisplayGraphIcons.SATELLITE_VIEW_ICON)
|
||||||
.onAction(this::toggleSatellite)
|
.onAction(this::toggleSatellite)
|
||||||
.selected(graphDisplayProvider.getDefaultSatelliteState())
|
.selected(graphDisplayProvider.getDefaultSatelliteState())
|
||||||
.buildAndInstallLocal(componentProvider);
|
.buildAndInstallLocal(componentProvider);
|
||||||
|
|
||||||
// create an icon button to reset the view transformations to identity (scaled to layout)
|
// create an icon button to reset the view transformations to identity (scaled to layout)
|
||||||
new ActionBuilder("Reset View", ACTION_OWNER).description("Fit Graph to Window")
|
new ActionBuilder("Reset View", ACTION_OWNER).description("Fit Graph to Window")
|
||||||
.toolBarIcon(DefaultDisplayGraphIcons.FIT_TO_WINDOW)
|
.toolBarIcon(DefaultDisplayGraphIcons.FIT_TO_WINDOW)
|
||||||
.onAction(context -> centerAndScale())
|
.onAction(context -> centerAndScale())
|
||||||
.buildAndInstallLocal(componentProvider);
|
.buildAndInstallLocal(componentProvider);
|
||||||
|
|
||||||
// create a button to show the view magnify lens
|
// create a button to show the view magnify lens
|
||||||
LensSupport<LensGraphMouse> magnifyViewSupport = createMagnifier();
|
LensSupport<LensGraphMouse> magnifyViewSupport = createMagnifier();
|
||||||
ToggleDockingAction lensToggle = new ToggleActionBuilder("View Magnifier", ACTION_OWNER)
|
ToggleDockingAction lensToggle = new ToggleActionBuilder("View Magnifier", ACTION_OWNER)
|
||||||
.description("Show View Magnifier")
|
.description("Show View Magnifier")
|
||||||
.toolBarIcon(DefaultDisplayGraphIcons.VIEW_MAGNIFIER_ICON)
|
.toolBarIcon(DefaultDisplayGraphIcons.VIEW_MAGNIFIER_ICON)
|
||||||
.onAction(context -> magnifyViewSupport
|
.onAction(context -> magnifyViewSupport
|
||||||
.activate(((AbstractButton) context.getSourceObject()).isSelected()))
|
.activate(((AbstractButton) context.getSourceObject()).isSelected()))
|
||||||
.build();
|
.build();
|
||||||
magnifyViewSupport.addItemListener(
|
magnifyViewSupport.addItemListener(
|
||||||
itemEvent -> lensToggle.setSelected(itemEvent.getStateChange() == ItemEvent.SELECTED));
|
itemEvent -> lensToggle.setSelected(itemEvent.getStateChange() == ItemEvent.SELECTED));
|
||||||
componentProvider.addLocalAction(lensToggle);
|
componentProvider.addLocalAction(lensToggle);
|
||||||
|
|
||||||
// create an action button to show a dialog with generated filters
|
// create an action button to show a dialog with generated filters
|
||||||
new ActionBuilder("Show Filters", ACTION_OWNER).description("Show Graph Filters")
|
new ActionBuilder("Show Filters", ACTION_OWNER).description("Show Graph Filters")
|
||||||
.toolBarIcon(DefaultDisplayGraphIcons.FILTER_ICON)
|
.toolBarIcon(DefaultDisplayGraphIcons.FILTER_ICON)
|
||||||
.onAction(context -> showFilterDialog())
|
.onAction(context -> showFilterDialog())
|
||||||
.buildAndInstallLocal(componentProvider);
|
.buildAndInstallLocal(componentProvider);
|
||||||
|
|
||||||
// create a menu with graph layout algorithm selections
|
// create a menu with graph layout algorithm selections
|
||||||
List<ActionState<String>> layoutActionStates = getLayoutActionStates();
|
List<ActionState<String>> layoutActionStates = getLayoutActionStates();
|
||||||
layoutAction = new MultiStateActionBuilder<String>("Arrangement", ACTION_OWNER)
|
layoutAction = new MultiStateActionBuilder<String>("Arrangement", ACTION_OWNER)
|
||||||
.description("Arrangement: " + layoutActionStates.get(0).getName())
|
.description("Arrangement: " + layoutActionStates.get(0).getName())
|
||||||
.toolBarIcon(DefaultDisplayGraphIcons.LAYOUT_ALGORITHM_ICON)
|
.toolBarIcon(DefaultDisplayGraphIcons.LAYOUT_ALGORITHM_ICON)
|
||||||
.useCheckboxForIcons(true)
|
.useCheckboxForIcons(true)
|
||||||
.onActionStateChanged((s, t) -> layoutChanged(s.getName()))
|
.onActionStateChanged((s, t) -> layoutChanged(s.getName()))
|
||||||
.addStates(layoutActionStates)
|
.addStates(layoutActionStates)
|
||||||
.buildAndInstallLocal(componentProvider);
|
.buildAndInstallLocal(componentProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createPopupActions() {
|
private void createPopupActions() {
|
||||||
new ActionBuilder("Select Vertex", ACTION_OWNER).popupMenuPath("Select Vertex")
|
new ActionBuilder("Select Vertex", ACTION_OWNER).popupMenuPath("Select Vertex")
|
||||||
.popupMenuGroup("selection", "1")
|
.popupMenuGroup("selection", "1")
|
||||||
.withContext(VertexGraphActionContext.class)
|
.withContext(VertexGraphActionContext.class)
|
||||||
.enabledWhen(c -> !isSelected(c.getClickedVertex()))
|
.enabledWhen(c -> !isSelected(c.getClickedVertex()))
|
||||||
.onAction(c -> viewer.getSelectedVertexState().select(c.getClickedVertex()))
|
.onAction(c -> viewer.getSelectedVertexState().select(c.getClickedVertex()))
|
||||||
.buildAndInstallLocal(componentProvider);
|
.buildAndInstallLocal(componentProvider);
|
||||||
|
|
||||||
new ActionBuilder("Deselect Vertex", ACTION_OWNER).popupMenuPath("Deselect Vertex")
|
new ActionBuilder("Deselect Vertex", ACTION_OWNER).popupMenuPath("Deselect Vertex")
|
||||||
.popupMenuGroup("selection", "2")
|
.popupMenuGroup("selection", "2")
|
||||||
.withContext(VertexGraphActionContext.class)
|
.withContext(VertexGraphActionContext.class)
|
||||||
.enabledWhen(c -> isSelected(c.getClickedVertex()))
|
.enabledWhen(c -> isSelected(c.getClickedVertex()))
|
||||||
.onAction(c -> viewer.getSelectedVertexState().deselect(c.getClickedVertex()))
|
.onAction(c -> viewer.getSelectedVertexState().deselect(c.getClickedVertex()))
|
||||||
.buildAndInstallLocal(componentProvider);
|
.buildAndInstallLocal(componentProvider);
|
||||||
|
|
||||||
new ActionBuilder("Select Edge", ACTION_OWNER).popupMenuPath("Select Edge")
|
new ActionBuilder("Select Edge", ACTION_OWNER).popupMenuPath("Select Edge")
|
||||||
.popupMenuGroup("selection", "1")
|
.popupMenuGroup("selection", "1")
|
||||||
.withContext(EdgeGraphActionContext.class)
|
.withContext(EdgeGraphActionContext.class)
|
||||||
.enabledWhen(c -> !isSelected(c.getClickedEdge()))
|
.enabledWhen(c -> !isSelected(c.getClickedEdge()))
|
||||||
.onAction(c -> selectEdge(c.getClickedEdge()))
|
.onAction(c -> selectEdge(c.getClickedEdge()))
|
||||||
.buildAndInstallLocal(componentProvider);
|
.buildAndInstallLocal(componentProvider);
|
||||||
|
|
||||||
new ActionBuilder("Deselect Edge", ACTION_OWNER).popupMenuPath("Deselect Edge")
|
new ActionBuilder("Deselect Edge", ACTION_OWNER).popupMenuPath("Deselect Edge")
|
||||||
.popupMenuGroup("selection", "2")
|
.popupMenuGroup("selection", "2")
|
||||||
.withContext(EdgeGraphActionContext.class)
|
.withContext(EdgeGraphActionContext.class)
|
||||||
.enabledWhen(c -> isSelected(c.getClickedEdge()))
|
.enabledWhen(c -> isSelected(c.getClickedEdge()))
|
||||||
.onAction(c -> deselectEdge(c.getClickedEdge()))
|
.onAction(c -> deselectEdge(c.getClickedEdge()))
|
||||||
.buildAndInstallLocal(componentProvider);
|
.buildAndInstallLocal(componentProvider);
|
||||||
|
|
||||||
new ActionBuilder("Edge Source", ACTION_OWNER).popupMenuPath("Go To Edge Source")
|
new ActionBuilder("Edge Source", ACTION_OWNER).popupMenuPath("Go To Edge Source")
|
||||||
.popupMenuGroup("Go To")
|
.popupMenuGroup("Go To")
|
||||||
.withContext(EdgeGraphActionContext.class)
|
.withContext(EdgeGraphActionContext.class)
|
||||||
.onAction(c -> {
|
.onAction(c -> {
|
||||||
selectEdge(c.getClickedEdge());
|
selectEdge(c.getClickedEdge());
|
||||||
setFocusedVertex(graph.getEdgeSource(c.getClickedEdge()));
|
setFocusedVertex(graph.getEdgeSource(c.getClickedEdge()));
|
||||||
})
|
})
|
||||||
.buildAndInstallLocal(componentProvider);
|
.buildAndInstallLocal(componentProvider);
|
||||||
|
|
||||||
new ActionBuilder("Edge Target", ACTION_OWNER).popupMenuPath("Go To Edge Target")
|
new ActionBuilder("Edge Target", ACTION_OWNER).popupMenuPath("Go To Edge Target")
|
||||||
.popupMenuGroup("Go To")
|
.popupMenuGroup("Go To")
|
||||||
.withContext(EdgeGraphActionContext.class)
|
.withContext(EdgeGraphActionContext.class)
|
||||||
.onAction(c -> {
|
.onAction(c -> {
|
||||||
selectEdge(c.getClickedEdge());
|
selectEdge(c.getClickedEdge());
|
||||||
setFocusedVertex(graph.getEdgeTarget(c.getClickedEdge()));
|
setFocusedVertex(graph.getEdgeTarget(c.getClickedEdge()));
|
||||||
})
|
})
|
||||||
.buildAndInstallLocal(componentProvider);
|
.buildAndInstallLocal(componentProvider);
|
||||||
|
|
||||||
hideSelectedAction =
|
hideSelectedAction =
|
||||||
new ToggleActionBuilder("Hide Selected", ACTION_OWNER).popupMenuPath("Hide Selected")
|
new ToggleActionBuilder("Hide Selected", ACTION_OWNER).popupMenuPath("Hide Selected")
|
||||||
.popupMenuGroup("z", "1")
|
.popupMenuGroup("z", "1")
|
||||||
|
.description("Toggles whether or not to show selected vertices and edges")
|
||||||
|
.onAction(c -> manageVertexDisplay())
|
||||||
|
.buildAndInstallLocal(componentProvider);
|
||||||
|
|
||||||
|
hideUnselectedAction = new ToggleActionBuilder("Hide Unselected", ACTION_OWNER)
|
||||||
|
.popupMenuPath("Hide Unselected")
|
||||||
|
.popupMenuGroup("z", "2")
|
||||||
.description("Toggles whether or not to show selected vertices and edges")
|
.description("Toggles whether or not to show selected vertices and edges")
|
||||||
.onAction(c -> manageVertexDisplay())
|
.onAction(c -> manageVertexDisplay())
|
||||||
.buildAndInstallLocal(componentProvider);
|
.buildAndInstallLocal(componentProvider);
|
||||||
|
|
||||||
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", ACTION_OWNER).popupMenuPath("Invert Selection")
|
new ActionBuilder("Invert Selection", ACTION_OWNER).popupMenuPath("Invert Selection")
|
||||||
.popupMenuGroup("z", "3")
|
.popupMenuGroup("z", "3")
|
||||||
.description("Inverts the current selection")
|
.description("Inverts the current selection")
|
||||||
.onAction(c -> invertSelection())
|
.onAction(c -> invertSelection())
|
||||||
.buildAndInstallLocal(componentProvider);
|
.buildAndInstallLocal(componentProvider);
|
||||||
|
|
||||||
new ActionBuilder("Grow Selection To Targets", ACTION_OWNER)
|
new ActionBuilder("Grow Selection To Targets", ACTION_OWNER)
|
||||||
.popupMenuPath("Grow Selection To Targets")
|
.popupMenuPath("Grow Selection To Targets")
|
||||||
.popupMenuGroup("z", "4")
|
.popupMenuGroup("z", "4")
|
||||||
.description("Extends the current selection by including the target vertex " +
|
.description("Extends the current selection by including the target vertex " +
|
||||||
"of all edges whose source is selected")
|
"of all edges whose source is selected")
|
||||||
.keyBinding("ctrl O")
|
.keyBinding("ctrl O")
|
||||||
.enabledWhen(c -> !isAllSelected(getTargetVerticesFromSelected()))
|
.enabledWhen(c -> !isAllSelected(getTargetVerticesFromSelected()))
|
||||||
.onAction(c -> growSelection(getTargetVerticesFromSelected()))
|
.onAction(c -> growSelection(getTargetVerticesFromSelected()))
|
||||||
.buildAndInstallLocal(componentProvider);
|
.buildAndInstallLocal(componentProvider);
|
||||||
|
|
||||||
new ActionBuilder("Grow Selection From Sources", ACTION_OWNER)
|
new ActionBuilder("Grow Selection From Sources", ACTION_OWNER)
|
||||||
.popupMenuPath("Grow Selection From Sources")
|
.popupMenuPath("Grow Selection From Sources")
|
||||||
.popupMenuGroup("z", "4")
|
.popupMenuGroup("z", "4")
|
||||||
.description("Extends the current selection by including the target vertex " +
|
.description("Extends the current selection by including the target vertex " +
|
||||||
"of all edges whose source is selected")
|
"of all edges whose source is selected")
|
||||||
.keyBinding("ctrl I")
|
.keyBinding("ctrl I")
|
||||||
.enabledWhen(c -> !isAllSelected(getSourceVerticesFromSelected()))
|
.enabledWhen(c -> !isAllSelected(getSourceVerticesFromSelected()))
|
||||||
.onAction(c -> growSelection(getSourceVerticesFromSelected()))
|
.onAction(c -> growSelection(getSourceVerticesFromSelected()))
|
||||||
.buildAndInstallLocal(componentProvider);
|
.buildAndInstallLocal(componentProvider);
|
||||||
|
|
||||||
new ActionBuilder("Grow Selection To Entire Component", ACTION_OWNER)
|
new ActionBuilder("Grow Selection To Entire Component", ACTION_OWNER)
|
||||||
.popupMenuPath("Grow Selection To Entire Component")
|
.popupMenuPath("Grow Selection To Entire Component")
|
||||||
.popupMenuGroup("z", "4")
|
.popupMenuGroup("z", "4")
|
||||||
.description(
|
.description(
|
||||||
"Extends the current selection by including the target/source vertices " +
|
"Extends the current selection by including the target/source vertices " +
|
||||||
"of all edges whose source/target is selected")
|
"of all edges whose source/target is selected")
|
||||||
.keyBinding("ctrl C")
|
.keyBinding("ctrl C")
|
||||||
.enabledWhen(c -> !isAllSelected(getSourceVerticesFromSelected()) ||
|
.enabledWhen(c -> !isAllSelected(getSourceVerticesFromSelected()) ||
|
||||||
!isAllSelected(getTargetVerticesFromSelected()))
|
!isAllSelected(getTargetVerticesFromSelected()))
|
||||||
.onAction(c -> growSelection(getAllComponentVerticesFromSelected()))
|
.onAction(c -> growSelection(getAllComponentVerticesFromSelected()))
|
||||||
.buildAndInstallLocal(componentProvider);
|
.buildAndInstallLocal(componentProvider);
|
||||||
|
|
||||||
new ActionBuilder("Clear Selection", ACTION_OWNER).popupMenuPath("Clear Selection")
|
new ActionBuilder("Clear Selection", ACTION_OWNER).popupMenuPath("Clear Selection")
|
||||||
.popupMenuGroup("z", "5")
|
.popupMenuGroup("z", "5")
|
||||||
.keyBinding("escape")
|
.keyBinding("escape")
|
||||||
.enabledWhen(c -> hasSelection())
|
.enabledWhen(c -> hasSelection())
|
||||||
.onAction(c -> clearSelection(true))
|
.onAction(c -> clearSelection(true))
|
||||||
.buildAndInstallLocal(componentProvider);
|
.buildAndInstallLocal(componentProvider);
|
||||||
|
|
||||||
new ActionBuilder("Create Subgraph", ACTION_OWNER)
|
new ActionBuilder("Create Subgraph", ACTION_OWNER)
|
||||||
.popupMenuPath("Display Selected as New Graph")
|
.popupMenuPath("Display Selected as New Graph")
|
||||||
.popupMenuGroup("zz", "5")
|
.popupMenuGroup("zz", "5")
|
||||||
.description("Creates a subgraph from the selected nodes")
|
.description("Creates a subgraph from the selected nodes")
|
||||||
.enabledWhen(c -> !viewer.getSelectedVertices().isEmpty())
|
.enabledWhen(c -> !viewer.getSelectedVertices().isEmpty())
|
||||||
.onAction(c -> createAndDisplaySubGraph())
|
.onAction(c -> createAndDisplaySubGraph())
|
||||||
.buildAndInstallLocal(componentProvider);
|
.buildAndInstallLocal(componentProvider);
|
||||||
|
|
||||||
new ActionBuilder("Collapse Selected", ACTION_OWNER)
|
new ActionBuilder("Collapse Selected", ACTION_OWNER)
|
||||||
.popupMenuPath("Collapse Selected Vertices")
|
.popupMenuPath("Collapse Selected Vertices")
|
||||||
.popupMenuGroup("zz", "6")
|
.popupMenuGroup("zz", "6")
|
||||||
.description("Collapses the selected vertices into one collapsed vertex")
|
.description("Collapses the selected vertices into one collapsed vertex")
|
||||||
.onAction(c -> groupSelectedVertices())
|
.onAction(c -> groupSelectedVertices())
|
||||||
.buildAndInstallLocal(componentProvider);
|
.buildAndInstallLocal(componentProvider);
|
||||||
|
|
||||||
new ActionBuilder("Expand Selected", ACTION_OWNER).popupMenuPath("Expand Selected Vertices")
|
new ActionBuilder("Expand Selected", ACTION_OWNER).popupMenuPath("Expand Selected Vertices")
|
||||||
.popupMenuGroup("zz", "6")
|
.popupMenuGroup("zz", "6")
|
||||||
.description("Expands all selected collapsed vertices into their previous form")
|
.description("Expands all selected collapsed vertices into their previous form")
|
||||||
.onAction(c -> ungroupSelectedVertices())
|
.onAction(c -> ungroupSelectedVertices())
|
||||||
.buildAndInstallLocal(componentProvider);
|
.buildAndInstallLocal(componentProvider);
|
||||||
|
|
||||||
new ActionBuilder("Graph Type Display Options", ACTION_OWNER)
|
new ActionBuilder("Graph Type Display Options", ACTION_OWNER)
|
||||||
.popupMenuPath("Graph Type Options ...")
|
.popupMenuPath("Graph Type Options ...")
|
||||||
.popupMenuGroup("zzz")
|
.popupMenuGroup("zzz")
|
||||||
.menuPath("Graph Type Options ...")
|
.menuPath("Graph Type Options ...")
|
||||||
.description("Brings up option editor for configuring vertex and edge types.")
|
.description("Brings up option editor for configuring vertex and edge types.")
|
||||||
.onAction(c -> editGraphDisplayOptions())
|
.onAction(c -> editGraphDisplayOptions())
|
||||||
.buildAndInstallLocal(componentProvider);
|
.buildAndInstallLocal(componentProvider);
|
||||||
|
|
||||||
togglePopupsAction = new ToggleActionBuilder("Display Popup Windows", ACTION_OWNER)
|
togglePopupsAction = new ToggleActionBuilder("Display Popup Windows", ACTION_OWNER)
|
||||||
.popupMenuPath("Display Popup Windows")
|
.popupMenuPath("Display Popup Windows")
|
||||||
.popupMenuGroup("zz", "1")
|
.popupMenuGroup("zz", "1")
|
||||||
.description("Toggles whether or not to show popup windows, such as tool tips")
|
.description("Toggles whether or not to show popup windows, such as tool tips")
|
||||||
.selected(true)
|
.selected(true)
|
||||||
.onAction(c -> popupRegulator.setPopupsVisible(togglePopupsAction.isSelected()))
|
.onAction(c -> popupRegulator.setPopupsVisible(togglePopupsAction.isSelected()))
|
||||||
.buildAndInstallLocal(componentProvider);
|
.buildAndInstallLocal(componentProvider);
|
||||||
popupRegulator.setPopupsVisible(togglePopupsAction.isSelected());
|
popupRegulator.setPopupsVisible(togglePopupsAction.isSelected());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -656,8 +655,8 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
MutableSelectedState<AttributedVertex> selectedVertexState =
|
MutableSelectedState<AttributedVertex> selectedVertexState =
|
||||||
viewer.getSelectedVertexState();
|
viewer.getSelectedVertexState();
|
||||||
return getSourceVerticesFromSelected().stream()
|
return getSourceVerticesFromSelected().stream()
|
||||||
.filter(v -> !selectedVertexState.isSelected(v))
|
.filter(v -> !selectedVertexState.isSelected(v))
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<AttributedVertex> getTargetVerticesFromSelected() {
|
private Set<AttributedVertex> getTargetVerticesFromSelected() {
|
||||||
|
@ -674,8 +673,8 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
MutableSelectedState<AttributedVertex> selectedVertexState =
|
MutableSelectedState<AttributedVertex> selectedVertexState =
|
||||||
viewer.getSelectedVertexState();
|
viewer.getSelectedVertexState();
|
||||||
return getTargetVerticesFromSelected().stream()
|
return getTargetVerticesFromSelected().stream()
|
||||||
.filter(v -> !selectedVertexState.isSelected(v))
|
.filter(v -> !selectedVertexState.isSelected(v))
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<AttributedVertex> getAllDownstreamVerticesFromSelected() {
|
private Set<AttributedVertex> getAllDownstreamVerticesFromSelected() {
|
||||||
|
@ -831,10 +830,12 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
public void close() {
|
public void close() {
|
||||||
graphDisplayProvider.remove(this);
|
graphDisplayProvider.remove(this);
|
||||||
if (listener != null) {
|
if (listener != null) {
|
||||||
listener.graphClosed();
|
listener.dispose();
|
||||||
|
listener = null;
|
||||||
}
|
}
|
||||||
listener = null;
|
|
||||||
componentProvider.closeComponent();
|
componentProvider.closeComponent();
|
||||||
|
|
||||||
if (graphDisplayOptions != null) {
|
if (graphDisplayOptions != null) {
|
||||||
graphDisplayOptions.removeChangeListener(graphDisplayOptionsChangeListener);
|
graphDisplayOptions.removeChangeListener(graphDisplayOptionsChangeListener);
|
||||||
}
|
}
|
||||||
|
@ -843,7 +844,9 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
@Override
|
@Override
|
||||||
public void setGraphDisplayListener(GraphDisplayListener listener) {
|
public void setGraphDisplayListener(GraphDisplayListener listener) {
|
||||||
if (this.listener != null) {
|
if (this.listener != null) {
|
||||||
this.listener.graphClosed();
|
// This is a bit odd to do here, but this seems like the easiest way to cleanup any
|
||||||
|
// previous listener when reusing the graph display.
|
||||||
|
this.listener.dispose();
|
||||||
}
|
}
|
||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
}
|
}
|
||||||
|
@ -895,10 +898,10 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
if (viewer.getComponent().isVisible() && !viewer.getBounds().isEmpty()) {
|
if (viewer.getComponent().isVisible() && !viewer.getBounds().isEmpty()) {
|
||||||
// project the view bounds into the layout coordinate system, test for containing the coordinates
|
// project the view bounds into the layout coordinate system, test for containing the coordinates
|
||||||
return viewer.getRenderContext()
|
return viewer.getRenderContext()
|
||||||
.getMultiLayerTransformer()
|
.getMultiLayerTransformer()
|
||||||
.inverseTransform(viewer.getBounds())
|
.inverseTransform(viewer.getBounds())
|
||||||
.getBounds()
|
.getBounds()
|
||||||
.contains(x, y);
|
.contains(x, y);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -975,12 +978,10 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
|
|
||||||
configureViewerPreferredSize();
|
configureViewerPreferredSize();
|
||||||
|
|
||||||
Swing.runNow(() -> {
|
// set the graph but defer the layout algorithm setting
|
||||||
// set the graph but defer the layout algorithm setting
|
viewer.getVisualizationModel().setGraph(graph, false);
|
||||||
viewer.getVisualizationModel().setGraph(graph, false);
|
configureFilters();
|
||||||
configureFilters();
|
setInitialLayoutAlgorithm();
|
||||||
setInitialLayoutAlgorithm();
|
|
||||||
});
|
|
||||||
componentProvider.setVisible(true);
|
componentProvider.setVisible(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1013,37 +1014,37 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
Set<AttributedVertex> vertices = graph.vertexSet();
|
Set<AttributedVertex> vertices = graph.vertexSet();
|
||||||
Set<AttributedEdge> edges = graph.edgeSet();
|
Set<AttributedEdge> edges = graph.edgeSet();
|
||||||
vertexFilters = AttributeFilters.builder()
|
vertexFilters = AttributeFilters.builder()
|
||||||
.exclude(Set.of("Address", "Code", "Name"))
|
.exclude(Set.of("Address", "Code", "Name"))
|
||||||
.elements(vertices)
|
.elements(vertices)
|
||||||
.maxFactor(.05)
|
.maxFactor(.05)
|
||||||
.buttonSupplier(JRadioButton::new)
|
.buttonSupplier(JRadioButton::new)
|
||||||
.paintFunction(v -> Colors.FOREGROUND)
|
.paintFunction(v -> Colors.FOREGROUND)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
vertexFilters.addItemListener(item -> {
|
vertexFilters.addItemListener(item -> {
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
Set<String> selected = (Set<String>) item.getItem();
|
Set<String> selected = (Set<String>) item.getItem();
|
||||||
viewer.getRenderContext()
|
viewer.getRenderContext()
|
||||||
.setVertexIncludePredicate(
|
.setVertexIncludePredicate(
|
||||||
v -> v.getAttributes().values().stream().noneMatch(selected::contains));
|
v -> v.getAttributes().values().stream().noneMatch(selected::contains));
|
||||||
viewer.repaint();
|
viewer.repaint();
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
edgeFilters = AttributeFilters.builder()
|
edgeFilters = AttributeFilters.builder()
|
||||||
.exclude(Set.of("*ToKey", "*FromKey", "Address", "Name"))
|
.exclude(Set.of("*ToKey", "*FromKey", "Address", "Name"))
|
||||||
.elements(edges)
|
.elements(edges)
|
||||||
.maxFactor(.01)
|
.maxFactor(.01)
|
||||||
.buttonSupplier(JRadioButton::new)
|
.buttonSupplier(JRadioButton::new)
|
||||||
.paintFunction(e -> Colors.FOREGROUND)
|
.paintFunction(e -> Colors.FOREGROUND)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
edgeFilters.addItemListener(item -> {
|
edgeFilters.addItemListener(item -> {
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
Set<String> selected = (Set<String>) item.getItem();
|
Set<String> selected = (Set<String>) item.getItem();
|
||||||
viewer.getRenderContext()
|
viewer.getRenderContext()
|
||||||
.setEdgeIncludePredicate(
|
.setEdgeIncludePredicate(
|
||||||
e -> e.getAttributes().values().stream().noneMatch(selected::contains));
|
e -> e.getAttributes().values().stream().noneMatch(selected::contains));
|
||||||
viewer.repaint();
|
viewer.repaint();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1059,14 +1060,14 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
// set the layoutModel's initials size to a minimal value. Not sure this should be necessary
|
// set the layoutModel's initials size to a minimal value. Not sure this should be necessary
|
||||||
// but it makes the initial scaling look better for small graphs. Otherwise it seems
|
// but it makes the initial scaling look better for small graphs. Otherwise it seems
|
||||||
// to use a very large area to layout the graph, resulting in tiny nodes that are spaced
|
// to use a very large area to layout the graph, resulting in tiny nodes that are spaced
|
||||||
// very far apart. This might just be a work around for a bug in some of the layout
|
// very far apart. This might just be a work around for a bug in some of the layout
|
||||||
// algorithms that don't seem to properly compute a good layout size.
|
// algorithms that don't seem to properly compute a good layout size.
|
||||||
viewer.getVisualizationModel().getLayoutModel().setSize(1, 1);
|
viewer.getVisualizationModel().getLayoutModel().setSize(1, 1);
|
||||||
|
|
||||||
if (vertexCount < 100) {
|
if (vertexCount < 100) {
|
||||||
viewer.getVisualizationModel()
|
viewer.getVisualizationModel()
|
||||||
.getLayoutModel()
|
.getLayoutModel()
|
||||||
.setPreferredSize(viewSize.width, viewSize.height);
|
.setPreferredSize(viewSize.width, viewSize.height);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
int newSize = viewSize.width + 5 * (vertexCount - 100);
|
int newSize = viewSize.width + 5 * (vertexCount - 100);
|
||||||
|
@ -1151,8 +1152,8 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
Point2D newCenter = getPointToCenter(vertices);
|
Point2D newCenter = getPointToCenter(vertices);
|
||||||
if (!isVisible(newCenter.getX(), newCenter.getY())) {
|
if (!isVisible(newCenter.getX(), newCenter.getY())) {
|
||||||
Point2D existingCenter = viewer.getRenderContext()
|
Point2D existingCenter = viewer.getRenderContext()
|
||||||
.getMultiLayerTransformer()
|
.getMultiLayerTransformer()
|
||||||
.inverseTransform(viewer.getCenter());
|
.inverseTransform(viewer.getCenter());
|
||||||
jobRunner.schedule(new CenterAnimationJob(viewer, existingCenter, newCenter));
|
jobRunner.schedule(new CenterAnimationJob(viewer, existingCenter, newCenter));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1215,12 +1216,12 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
protected VisualizationViewer<AttributedVertex, AttributedEdge> createViewer() {
|
protected VisualizationViewer<AttributedVertex, AttributedEdge> createViewer() {
|
||||||
VisualizationViewer<AttributedVertex, AttributedEdge> vv =
|
VisualizationViewer<AttributedVertex, AttributedEdge> vv =
|
||||||
VisualizationViewer.<AttributedVertex, AttributedEdge> builder()
|
VisualizationViewer.<AttributedVertex, AttributedEdge> builder()
|
||||||
.multiSelectionStrategySupplier(
|
.multiSelectionStrategySupplier(
|
||||||
() -> freeFormSelection ? MultiSelectionStrategy.arbitrary()
|
() -> freeFormSelection ? MultiSelectionStrategy.arbitrary()
|
||||||
: MultiSelectionStrategy.rectangular())
|
: MultiSelectionStrategy.rectangular())
|
||||||
.viewSize(PREFERRED_VIEW_SIZE)
|
.viewSize(PREFERRED_VIEW_SIZE)
|
||||||
.layoutSize(PREFERRED_LAYOUT_SIZE)
|
.layoutSize(PREFERRED_LAYOUT_SIZE)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
// Add an ancestor listener to scale and center the graph after the component
|
// Add an ancestor listener to scale and center the graph after the component
|
||||||
// has been initially shown.
|
// has been initially shown.
|
||||||
|
@ -1310,7 +1311,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
}
|
}
|
||||||
|
|
||||||
addedActions.add(action);
|
addedActions.add(action);
|
||||||
Swing.runLater(() -> componentProvider.addLocalAction(action));
|
componentProvider.addLocalAction(action);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1361,7 +1362,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
}
|
}
|
||||||
else if (hideSelected) {
|
else if (hideSelected) {
|
||||||
viewer.getRenderContext()
|
viewer.getRenderContext()
|
||||||
.setVertexIncludePredicate(Predicate.not(selectedVertexState::isSelected));
|
.setVertexIncludePredicate(Predicate.not(selectedVertexState::isSelected));
|
||||||
}
|
}
|
||||||
else if (hideUnselected) {
|
else if (hideUnselected) {
|
||||||
viewer.getRenderContext().setVertexIncludePredicate(selectedVertexState::isSelected);
|
viewer.getRenderContext().setVertexIncludePredicate(selectedVertexState::isSelected);
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
package ghidra.graph.visualization;
|
package ghidra.graph.visualization;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.concurrent.CopyOnWriteArraySet;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import ghidra.framework.options.Options;
|
import ghidra.framework.options.Options;
|
||||||
|
@ -30,7 +31,7 @@ public class DefaultGraphDisplayProvider implements GraphDisplayProvider {
|
||||||
|
|
||||||
private static final String PREFERENCES_KEY = "GRAPH_DISPLAY_SERVICE";
|
private static final String PREFERENCES_KEY = "GRAPH_DISPLAY_SERVICE";
|
||||||
private static final String DEFAULT_SATELLITE_STATE = "DEFAULT_SATELLITE_STATE";
|
private static final String DEFAULT_SATELLITE_STATE = "DEFAULT_SATELLITE_STATE";
|
||||||
private final Set<DefaultGraphDisplay> displays = new HashSet<>();
|
private final Set<DefaultGraphDisplayWrapper> displays = new CopyOnWriteArraySet<>();
|
||||||
private PluginTool pluginTool;
|
private PluginTool pluginTool;
|
||||||
private Options options;
|
private Options options;
|
||||||
private int displayCounter = 1;
|
private int displayCounter = 1;
|
||||||
|
@ -52,16 +53,22 @@ public class DefaultGraphDisplayProvider implements GraphDisplayProvider {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GraphDisplay getGraphDisplay(boolean reuseGraph, TaskMonitor monitor) {
|
public GraphDisplay getGraphDisplay(boolean reuseGraph, TaskMonitor monitor) {
|
||||||
|
|
||||||
return Swing.runNow(() -> {
|
return Swing.runNow(() -> {
|
||||||
|
|
||||||
if (reuseGraph && !displays.isEmpty()) {
|
if (reuseGraph && !displays.isEmpty()) {
|
||||||
DefaultGraphDisplay visibleGraph = (DefaultGraphDisplay) getActiveGraphDisplay();
|
DefaultGraphDisplayWrapper visibleGraph =
|
||||||
|
(DefaultGraphDisplayWrapper) getActiveGraphDisplay();
|
||||||
|
|
||||||
|
// set a temporary dummy graph; clients will set a real graph
|
||||||
visibleGraph.setGraph(new AttributedGraph("Empty", null),
|
visibleGraph.setGraph(new AttributedGraph("Empty", null),
|
||||||
new DefaultGraphDisplayOptions(), "", false, monitor);
|
new DefaultGraphDisplayOptions(), "", false, monitor);
|
||||||
visibleGraph.restoreToDefaultSetOfActions();
|
visibleGraph.restoreDefaultState();
|
||||||
return visibleGraph;
|
return visibleGraph;
|
||||||
}
|
}
|
||||||
|
|
||||||
DefaultGraphDisplay display = new DefaultGraphDisplay(this, displayCounter++);
|
DefaultGraphDisplayWrapper display =
|
||||||
|
new DefaultGraphDisplayWrapper(this, displayCounter++);
|
||||||
displays.add(display);
|
displays.add(display);
|
||||||
return display;
|
return display;
|
||||||
});
|
});
|
||||||
|
@ -72,27 +79,28 @@ public class DefaultGraphDisplayProvider implements GraphDisplayProvider {
|
||||||
if (displays.isEmpty()) {
|
if (displays.isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get the sorted displays in order to pick the newest graph
|
||||||
return getAllGraphDisplays().get(0);
|
return getAllGraphDisplays().get(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<GraphDisplay> getAllGraphDisplays() {
|
public List<GraphDisplay> getAllGraphDisplays() {
|
||||||
return Swing.runNow(() -> {
|
return displays.stream().sorted().collect(Collectors.toList());
|
||||||
return displays.stream()
|
|
||||||
.sorted((d1, d2) -> -(d1.getId() - d2.getId())) // largest/newest IDs come first
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initialize(PluginTool tool, Options graphOptions) {
|
public void initialize(PluginTool tool, Options graphOptions) {
|
||||||
this.pluginTool = tool;
|
this.pluginTool = tool;
|
||||||
this.options = graphOptions;
|
this.options = graphOptions;
|
||||||
|
|
||||||
|
Swing.assertSwingThread("Graph preferences must be accessed on the Swing thread");
|
||||||
preferences = pluginTool.getWindowManager().getPreferenceState(PREFERENCES_KEY);
|
preferences = pluginTool.getWindowManager().getPreferenceState(PREFERENCES_KEY);
|
||||||
if (preferences == null) {
|
if (preferences == null) {
|
||||||
preferences = new PreferenceState();
|
preferences = new PreferenceState();
|
||||||
pluginTool.getWindowManager().putPreferenceState(PREFERENCES_KEY, preferences);
|
pluginTool.getWindowManager().putPreferenceState(PREFERENCES_KEY, preferences);
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultSatelliteState = preferences.getBoolean(DEFAULT_SATELLITE_STATE, false);
|
defaultSatelliteState = preferences.getBoolean(DEFAULT_SATELLITE_STATE, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,11 +111,12 @@ public class DefaultGraphDisplayProvider implements GraphDisplayProvider {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void dispose() {
|
public void dispose() {
|
||||||
// first copy to new set to avoid concurrent modification exception
|
|
||||||
HashSet<DefaultGraphDisplay> set = new HashSet<>(displays);
|
// Calling close() will trigger the display to call back to this class's remove(). Avoid
|
||||||
for (DefaultGraphDisplay display : set) {
|
// unnecessary copies in the 'copy on write' set by closing after clearing the set.
|
||||||
display.close();
|
Set<GraphDisplay> set = new HashSet<>(displays);
|
||||||
}
|
displays.clear();
|
||||||
|
set.forEach(d -> d.close());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -115,8 +124,8 @@ public class DefaultGraphDisplayProvider implements GraphDisplayProvider {
|
||||||
return new HelpLocation("GraphServices", "Default_Graph_Display");
|
return new HelpLocation("GraphServices", "Default_Graph_Display");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void remove(DefaultGraphDisplay defaultGraphDisplay) {
|
void remove(DefaultGraphDisplay defaultGraphDisplay) {
|
||||||
displays.remove(defaultGraphDisplay);
|
displays.removeIf(wrapper -> wrapper.isDelegate(defaultGraphDisplay));
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean getDefaultSatelliteState() {
|
boolean getDefaultSatelliteState() {
|
||||||
|
@ -124,9 +133,8 @@ public class DefaultGraphDisplayProvider implements GraphDisplayProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
void setDefaultSatelliteState(boolean b) {
|
void setDefaultSatelliteState(boolean b) {
|
||||||
|
Swing.assertSwingThread("Graph preferences must be accessed on the Swing thread");
|
||||||
defaultSatelliteState = b;
|
defaultSatelliteState = b;
|
||||||
preferences.putBoolean(DEFAULT_SATELLITE_STATE, b);
|
preferences.putBoolean(DEFAULT_SATELLITE_STATE, b);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,118 @@
|
||||||
|
/* ###
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import docking.action.DockingActionIf;
|
||||||
|
import docking.widgets.EventTrigger;
|
||||||
|
import ghidra.service.graph.*;
|
||||||
|
import ghidra.util.Swing;
|
||||||
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link DefaultGraphDisplay} wrapper created to ensure all accesses to the delegate are on the
|
||||||
|
* Swing thread. This API is meant to be used concurrently. We do not want to force clients to
|
||||||
|
* have to understand when to use the Swing thread. Thus, this class handles that for the clients.
|
||||||
|
* Also, by having Swing accesses managed here, the {@link DefaultGraphDisplay} can assume that all
|
||||||
|
* its work will be done on the Swing thread.
|
||||||
|
*/
|
||||||
|
public class DefaultGraphDisplayWrapper
|
||||||
|
implements GraphDisplay, Comparable<DefaultGraphDisplayWrapper> {
|
||||||
|
|
||||||
|
private DefaultGraphDisplay delegate;
|
||||||
|
|
||||||
|
DefaultGraphDisplayWrapper(DefaultGraphDisplayProvider displayProvider, int id) {
|
||||||
|
delegate = Swing.runNow(() -> new DefaultGraphDisplay(displayProvider, id));
|
||||||
|
}
|
||||||
|
|
||||||
|
void restoreDefaultState() {
|
||||||
|
Swing.runNow(() -> delegate.restoreToDefaultSetOfActions());
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isDelegate(DefaultGraphDisplay other) {
|
||||||
|
return other == delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setGraphDisplayListener(GraphDisplayListener listener) {
|
||||||
|
Swing.runNow(() -> delegate.setGraphDisplayListener(listener));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setFocusedVertex(AttributedVertex vertex, EventTrigger eventTrigger) {
|
||||||
|
Swing.runNow(() -> delegate.setFocusedVertex(vertex));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AttributedGraph getGraph() {
|
||||||
|
return Swing.runNow(() -> delegate.getGraph());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AttributedVertex getFocusedVertex() {
|
||||||
|
return Swing.runNow(() -> delegate.getFocusedVertex());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void selectVertices(Set<AttributedVertex> vertexSet, EventTrigger eventTrigger) {
|
||||||
|
Swing.runNow(() -> delegate.selectVertices(vertexSet, eventTrigger));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<AttributedVertex> getSelectedVertices() {
|
||||||
|
return Swing.runNow(() -> delegate.getSelectedVertices());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
Swing.runNow(() -> delegate.close());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setGraph(AttributedGraph graph, GraphDisplayOptions options, String title,
|
||||||
|
boolean append, TaskMonitor monitor) {
|
||||||
|
Swing.runNow(() -> delegate.setGraph(graph, options, title, append, monitor));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
Swing.runNow(() -> delegate.clear());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateVertexName(AttributedVertex vertex, String newName) {
|
||||||
|
Swing.runNow(() -> delegate.updateVertexName(vertex, newName));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getGraphTitle() {
|
||||||
|
return Swing.runNow(() -> delegate.getGraphTitle());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addAction(DockingActionIf action) {
|
||||||
|
Swing.runNow(() -> delegate.addAction(action));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(DefaultGraphDisplayWrapper other) {
|
||||||
|
// note: no need for call to Swing, assuming ID is immutable
|
||||||
|
|
||||||
|
// larger/newer values are preferred so they should be first when sorting
|
||||||
|
return -(delegate.getId() - other.delegate.getId());
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,7 +21,6 @@ import java.util.*;
|
||||||
|
|
||||||
import docking.action.builder.ActionBuilder;
|
import docking.action.builder.ActionBuilder;
|
||||||
import docking.widgets.EventTrigger;
|
import docking.widgets.EventTrigger;
|
||||||
import ghidra.app.plugin.core.colorizer.ColorizingService;
|
|
||||||
import ghidra.app.util.AddEditDialog;
|
import ghidra.app.util.AddEditDialog;
|
||||||
import ghidra.framework.plugintool.PluginTool;
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
import ghidra.graph.*;
|
import ghidra.graph.*;
|
||||||
|
@ -54,8 +53,6 @@ public class BlockGraphTask extends Task {
|
||||||
private boolean showCode = false;
|
private boolean showCode = false;
|
||||||
private int codeLimitPerBlock = 10;
|
private int codeLimitPerBlock = 10;
|
||||||
|
|
||||||
private ColorizingService colorizingService;
|
|
||||||
|
|
||||||
private final static String ENTRY_NEXUS_NAME = "Entry Points";
|
private final static String ENTRY_NEXUS_NAME = "Entry Points";
|
||||||
private static final int MAX_SYMBOLS = 10;
|
private static final int MAX_SYMBOLS = 10;
|
||||||
private CodeBlockModel blockModel;
|
private CodeBlockModel blockModel;
|
||||||
|
@ -70,10 +67,10 @@ public class BlockGraphTask extends Task {
|
||||||
private String graphTitle;
|
private String graphTitle;
|
||||||
private ProgramGraphType graphType;
|
private ProgramGraphType graphType;
|
||||||
|
|
||||||
public BlockGraphTask(ProgramGraphType graphType,
|
public BlockGraphTask(ProgramGraphType graphType, boolean graphEntryPointNexus,
|
||||||
boolean graphEntryPointNexus, boolean reuseGraph, boolean appendGraph,
|
boolean reuseGraph, boolean appendGraph, PluginTool tool, ProgramSelection selection,
|
||||||
PluginTool tool, ProgramSelection selection, ProgramLocation location,
|
ProgramLocation location, CodeBlockModel blockModel,
|
||||||
CodeBlockModel blockModel, GraphDisplayProvider graphProvider) {
|
GraphDisplayProvider graphProvider) {
|
||||||
|
|
||||||
super("Graph Program", true, false, true);
|
super("Graph Program", true, false, true);
|
||||||
this.graphType = graphType;
|
this.graphType = graphType;
|
||||||
|
@ -84,24 +81,19 @@ public class BlockGraphTask extends Task {
|
||||||
this.tool = tool;
|
this.tool = tool;
|
||||||
this.blockModel = blockModel;
|
this.blockModel = blockModel;
|
||||||
this.graphProvider = graphProvider;
|
this.graphProvider = graphProvider;
|
||||||
this.colorizingService = tool.getService(ColorizingService.class);
|
|
||||||
this.selection = selection;
|
this.selection = selection;
|
||||||
this.location = location;
|
this.location = location;
|
||||||
this.program = blockModel.getProgram();
|
this.program = blockModel.getProgram();
|
||||||
this.graphTitle = graphType.getName() + ": ";
|
this.graphTitle = graphType.getName() + ": ";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Runs the move memory operation.
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void run(TaskMonitor monitor) throws CancelledException {
|
public void run(TaskMonitor monitor) throws CancelledException {
|
||||||
this.graphScope = getGraphScopeAndGenerateGraphTitle();
|
this.graphScope = getGraphScopeAndGenerateGraphTitle();
|
||||||
AttributedGraph graph = createGraph(graphTitle);
|
AttributedGraph graph = createGraph(graphTitle);
|
||||||
monitor.setMessage("Generating Graph...");
|
monitor.setMessage("Generating Graph...");
|
||||||
try {
|
try {
|
||||||
GraphDisplay display =
|
GraphDisplay display = graphProvider.getGraphDisplay(reuseGraph, monitor);
|
||||||
graphProvider.getGraphDisplay(reuseGraph, monitor);
|
|
||||||
GraphDisplayOptions graphOptions = new ProgramGraphDisplayOptions(graphType, tool);
|
GraphDisplayOptions graphOptions = new ProgramGraphDisplayOptions(graphType, tool);
|
||||||
if (showCode) { // arrows need to be bigger as this generates larger vertices
|
if (showCode) { // arrows need to be bigger as this generates larger vertices
|
||||||
graphOptions.setArrowLength(30);
|
graphOptions.setArrowLength(30);
|
||||||
|
@ -140,14 +132,14 @@ public class BlockGraphTask extends Task {
|
||||||
private void addActions(GraphDisplay display,
|
private void addActions(GraphDisplay display,
|
||||||
java.util.function.Function<AttributedVertex, Address> addressFunction) {
|
java.util.function.Function<AttributedVertex, Address> addressFunction) {
|
||||||
|
|
||||||
display.addAction(new ActionBuilder("Rename Symbol", "Block Graph")
|
display.addAction(
|
||||||
.popupMenuPath("Rename Symbol")
|
new ActionBuilder("Rename Symbol", "Block Graph").popupMenuPath("Rename Symbol")
|
||||||
.withContext(VertexGraphActionContext.class)
|
.withContext(VertexGraphActionContext.class)
|
||||||
.helpLocation(new HelpLocation("ProgramGraphPlugin", "Rename_Symbol"))
|
.helpLocation(new HelpLocation("ProgramGraphPlugin", "Rename_Symbol"))
|
||||||
// only enable action when vertex corresponds to an address
|
// only enable action when vertex corresponds to an address
|
||||||
.enabledWhen(c -> addressFunction.apply(c.getClickedVertex()) != null)
|
.enabledWhen(c -> addressFunction.apply(c.getClickedVertex()) != null)
|
||||||
.onAction(c -> updateVertexName(addressFunction, c))
|
.onAction(c -> updateVertexName(addressFunction, c))
|
||||||
.build());
|
.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateVertexName(
|
private void updateVertexName(
|
||||||
|
@ -264,8 +256,7 @@ public class BlockGraphTask extends Task {
|
||||||
}
|
}
|
||||||
|
|
||||||
private Address graphBlock(AttributedGraph graph, CodeBlock curBB,
|
private Address graphBlock(AttributedGraph graph, CodeBlock curBB,
|
||||||
List<AttributedVertex> entries)
|
List<AttributedVertex> entries) throws CancelledException {
|
||||||
throws CancelledException {
|
|
||||||
|
|
||||||
Address[] startAddrs = curBB.getStartAddresses();
|
Address[] startAddrs = curBB.getStartAddresses();
|
||||||
|
|
||||||
|
@ -428,7 +419,7 @@ public class BlockGraphTask extends Task {
|
||||||
if (count != 0) {
|
if (count != 0) {
|
||||||
buf.append('\n');
|
buf.append('\n');
|
||||||
}
|
}
|
||||||
// limit the number of symbols to include (there can be a ridiculous # of symbols)
|
// limit the number of symbols to include (there can be a ridiculous # of symbols)
|
||||||
if (count++ > MAX_SYMBOLS) {
|
if (count++ > MAX_SYMBOLS) {
|
||||||
buf.append("...");
|
buf.append("...");
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -26,11 +26,11 @@ import org.jgrapht.graph.DefaultGraphType;
|
||||||
* Basic graph implementation for a directed graph whose vertices and edges support attributes.
|
* Basic graph implementation for a directed graph whose vertices and edges support attributes.
|
||||||
* <P>
|
* <P>
|
||||||
* The graph can be configured as to how to handle multiple edges with the same source and destination
|
* The graph can be configured as to how to handle multiple edges with the same source and destination
|
||||||
* vertices. One option is to simply allow multiple edges. The second option is to collapse
|
* vertices. One option is to simply allow multiple edges. The second option is to collapse
|
||||||
* duplicate edges such that there is only ever one edge with the same
|
* duplicate edges such that there is only ever one edge with the same
|
||||||
* source and destination. In this case, each additional duplicate edge added will cause the
|
* source and destination. In this case, each additional duplicate edge added will cause the
|
||||||
* edge to have a "Weight" attribute that will be the total number of edges that were added
|
* edge to have a "Weight" attribute that will be the total number of edges that were added
|
||||||
* to the same source/destination vertex pair.
|
* to the same source/destination vertex pair.
|
||||||
*/
|
*/
|
||||||
public class AttributedGraph extends AbstractBaseGraph<AttributedVertex, AttributedEdge> {
|
public class AttributedGraph extends AbstractBaseGraph<AttributedVertex, AttributedEdge> {
|
||||||
public static final String WEIGHT = "Weight";
|
public static final String WEIGHT = "Weight";
|
||||||
|
@ -46,7 +46,7 @@ public class AttributedGraph extends AbstractBaseGraph<AttributedVertex, Attribu
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new empty AttributedGraph that automatically collapses duplicate edges
|
* Create a new empty AttributedGraph that automatically collapses duplicate edges
|
||||||
*
|
*
|
||||||
* @param name the name of the graph
|
* @param name the name of the graph
|
||||||
* @param type the {@link GraphType} which defines valid vertex and edge types.
|
* @param type the {@link GraphType} which defines valid vertex and edge types.
|
||||||
*/
|
*/
|
||||||
|
@ -56,7 +56,7 @@ public class AttributedGraph extends AbstractBaseGraph<AttributedVertex, Attribu
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new empty AttributedGraph that automatically collapses duplicate edges
|
* Create a new empty AttributedGraph that automatically collapses duplicate edges
|
||||||
*
|
*
|
||||||
* @param name the name of the graph
|
* @param name the name of the graph
|
||||||
* @param type the {@link GraphType} which defines valid vertex and edge types.
|
* @param type the {@link GraphType} which defines valid vertex and edge types.
|
||||||
* @param description a description of the graph
|
* @param description a description of the graph
|
||||||
|
@ -111,7 +111,7 @@ public class AttributedGraph extends AbstractBaseGraph<AttributedVertex, Attribu
|
||||||
* Adds a new vertex with the given id. The vertex's name will be the same as the id.
|
* Adds a new vertex with the given id. The vertex's name will be the same as the id.
|
||||||
* If a vertex already exists with that id,
|
* If a vertex already exists with that id,
|
||||||
* then that vertex will be returned.
|
* then that vertex will be returned.
|
||||||
*
|
*
|
||||||
* @param id the unique vertex id that the graph should have a vertex for.
|
* @param id the unique vertex id that the graph should have a vertex for.
|
||||||
* @return either an existing vertex with that id, or a newly added vertex with that id
|
* @return either an existing vertex with that id, or a newly added vertex with that id
|
||||||
*/
|
*/
|
||||||
|
@ -122,18 +122,18 @@ public class AttributedGraph extends AbstractBaseGraph<AttributedVertex, Attribu
|
||||||
/**
|
/**
|
||||||
* Adds a new vertex with the given id and name. If a vertex already exists with that id,
|
* Adds a new vertex with the given id and name. If a vertex already exists with that id,
|
||||||
* then that vertex will be returned, but with its name changed to the given name.
|
* then that vertex will be returned, but with its name changed to the given name.
|
||||||
*
|
*
|
||||||
* @param id the unique vertex id that the graph should have a vertex for.
|
* @param id the unique vertex id that the graph should have a vertex for.
|
||||||
* @param name the name to associate with this vertex
|
* @param vertexName the name to associate with this vertex
|
||||||
* @return either an existing vertex with that id, or a newly added vertex with that id
|
* @return either an existing vertex with that id, or a newly added vertex with that id
|
||||||
*/
|
*/
|
||||||
public AttributedVertex addVertex(String id, String name) {
|
public AttributedVertex addVertex(String id, String vertexName) {
|
||||||
if (vertexMap.containsKey(id)) {
|
if (vertexMap.containsKey(id)) {
|
||||||
AttributedVertex vertex = vertexMap.get(id);
|
AttributedVertex vertex = vertexMap.get(id);
|
||||||
vertex.setName(name);
|
vertex.setName(vertexName);
|
||||||
return vertex;
|
return vertex;
|
||||||
}
|
}
|
||||||
AttributedVertex newVertex = new AttributedVertex(id, name);
|
AttributedVertex newVertex = new AttributedVertex(id, vertexName);
|
||||||
addVertex(newVertex);
|
addVertex(newVertex);
|
||||||
return newVertex;
|
return newVertex;
|
||||||
}
|
}
|
||||||
|
@ -159,7 +159,7 @@ public class AttributedGraph extends AbstractBaseGraph<AttributedVertex, Attribu
|
||||||
* target vertices. If the graph is set to collapse duplicate edges and an edge for that
|
* target vertices. If the graph is set to collapse duplicate edges and an edge for that
|
||||||
* source and target exists, then the existing edge will be return with its "Weight" attribute
|
* source and target exists, then the existing edge will be return with its "Weight" attribute
|
||||||
* set to the total number of edges that have been added between the source and target vertices.
|
* set to the total number of edges that have been added between the source and target vertices.
|
||||||
*
|
*
|
||||||
* @param source the source vertex of the directed edge to be created.
|
* @param source the source vertex of the directed edge to be created.
|
||||||
* @param target the target vertex of the directed edge to be created.
|
* @param target the target vertex of the directed edge to be created.
|
||||||
* @param edgeId the id to use for the new edge. Note: if this is a duplicate and edges
|
* @param edgeId the id to use for the new edge. Note: if this is a duplicate and edges
|
||||||
|
@ -178,7 +178,7 @@ public class AttributedGraph extends AbstractBaseGraph<AttributedVertex, Attribu
|
||||||
* collapse duplicate edges and an edge for that
|
* collapse duplicate edges and an edge for that
|
||||||
* source and target exists, then the existing edge will be return with its "Weight" attribute
|
* source and target exists, then the existing edge will be return with its "Weight" attribute
|
||||||
* set to the total number of edges that have been added between the source and target vertices.
|
* set to the total number of edges that have been added between the source and target vertices.
|
||||||
*
|
*
|
||||||
* @param source the source vertex of the directed edge to be created.
|
* @param source the source vertex of the directed edge to be created.
|
||||||
* @param target the target vertex of the directed edge to be created.
|
* @param target the target vertex of the directed edge to be created.
|
||||||
* @param edge the BasicEdge object to use for the new edge. Note: if this is a duplicate and
|
* @param edge the BasicEdge object to use for the new edge. Note: if this is a duplicate and
|
||||||
|
@ -205,7 +205,7 @@ public class AttributedGraph extends AbstractBaseGraph<AttributedVertex, Attribu
|
||||||
* target vertices. If the graph is set to collapse duplicate edges and an edge for that
|
* target vertices. If the graph is set to collapse duplicate edges and an edge for that
|
||||||
* source and target exists, then the existing edge will be return with its "Weight" attribute
|
* source and target exists, then the existing edge will be return with its "Weight" attribute
|
||||||
* set to the total number of edges that have been added between the source and target vertices.
|
* set to the total number of edges that have been added between the source and target vertices.
|
||||||
*
|
*
|
||||||
* @param source the source vertex of the directed edge to be created.
|
* @param source the source vertex of the directed edge to be created.
|
||||||
* @param target the target vertex of the directed edge to be created.
|
* @param target the target vertex of the directed edge to be created.
|
||||||
* @return a new edge between the source and target if it is the first one or the graph is
|
* @return a new edge between the source and target if it is the first one or the graph is
|
||||||
|
|
|
@ -19,11 +19,6 @@ import java.util.Set;
|
||||||
|
|
||||||
public class DummyGraphDisplayListener implements GraphDisplayListener {
|
public class DummyGraphDisplayListener implements GraphDisplayListener {
|
||||||
|
|
||||||
@Override
|
|
||||||
public void graphClosed() {
|
|
||||||
// I'm a dummy
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GraphDisplayListener cloneWith(GraphDisplay graphDisplay) {
|
public GraphDisplayListener cloneWith(GraphDisplay graphDisplay) {
|
||||||
return new DummyGraphDisplayListener();
|
return new DummyGraphDisplayListener();
|
||||||
|
@ -31,17 +26,17 @@ public class DummyGraphDisplayListener implements GraphDisplayListener {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void selectionChanged(Set<AttributedVertex> vertices) {
|
public void selectionChanged(Set<AttributedVertex> vertices) {
|
||||||
// I'm a dummy
|
// stub
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void locationFocusChanged(AttributedVertex vertex) {
|
public void locationFocusChanged(AttributedVertex vertex) {
|
||||||
// I'm a dummy
|
// stub
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void dispose() {
|
public void dispose() {
|
||||||
// I'm a dummy
|
// stub
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,74 +30,18 @@ import ghidra.util.task.TaskMonitor;
|
||||||
* closed.
|
* closed.
|
||||||
*/
|
*/
|
||||||
public interface GraphDisplay {
|
public interface GraphDisplay {
|
||||||
public static final int ALIGN_LEFT = 0; // aligns graph text to the left
|
|
||||||
public static final int ALIGN_CENTER = 1; // aligns graph text to the center
|
|
||||||
public static final int ALIGN_RIGHT = 2; // aligns graph text to the right
|
|
||||||
|
|
||||||
/**
|
|
||||||
* values are color names or rgb in hex '0xFF0000' is red
|
|
||||||
*/
|
|
||||||
public static final String SELECTED_VERTEX_COLOR = "selectedVertexColor";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* values are color names or rgb in hex '0xFF0000' is red
|
|
||||||
*/
|
|
||||||
public static final String SELECTED_EDGE_COLOR = "selectedEdgeColor";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* values are defined as String symbols in LayoutFunction class
|
|
||||||
*
|
|
||||||
* KAMADA_KAWAI,FRUCTERMAN_REINGOLD,CIRCLE_MINCROSS,TIDIER_TREE,TIDIER_RADIAL_TREE,
|
|
||||||
* MIN_CROSS_TOP_DOWN,MIN_CROSS_LONGEST_PATH,MIN_CROSS_NETWORK_SIMPLEX,MIN_CROSS_COFFMAN_GRAHAM,
|
|
||||||
* EXP_MIN_CROSS_TOP_DOWN,EXP_MIN_CROSS_LONGEST_PATH,EXP_MIN_CROSS_NETWORK_SIMPLEX,
|
|
||||||
* EXP_MIN_CROSS_COFFMAN_GRAHAM,TREE,RADIAL,BALLOON,GEM
|
|
||||||
*
|
|
||||||
* may have no meaning for a different graph visualization library
|
|
||||||
*/
|
|
||||||
public static final String INITIAL_LAYOUT_ALGORITHM = "initialLayoutAlgorithm";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* true or false
|
|
||||||
* may have no meaning for a different graph visualization library
|
|
||||||
*/
|
|
||||||
public static final String DISPLAY_VERTICES_AS_ICONS = "displayVerticesAsIcons";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* values are the strings N,NE,E,SE,S,SW,W,NW,AUTO,CNTR
|
|
||||||
* may have no meaning for a different graph visualization library
|
|
||||||
*/
|
|
||||||
public static final String VERTEX_LABEL_POSITION = "vertexLabelPosition";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* true or false, whether edge selection via a mouse click is enabled.
|
|
||||||
* May not be supported by another graph visualization library
|
|
||||||
*/
|
|
||||||
public static final String ENABLE_EDGE_SELECTION = "enableEdgeSelection";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* a comma-separated list of edge type names in priority order
|
|
||||||
*/
|
|
||||||
public static final String EDGE_TYPE_PRIORITY_LIST = "edgeTypePriorityList";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* a comma-separated list of edge type names.
|
|
||||||
* any will be considered a favored edge for the min-cross layout
|
|
||||||
* algorithms.
|
|
||||||
* May have no meaning with a different graph visualization library
|
|
||||||
*/
|
|
||||||
public static final String FAVORED_EDGES = "favoredEdges";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets a {@link GraphDisplayListener} to be notified when the user changes the vertex focus
|
* Sets a {@link GraphDisplayListener} to be notified when the user changes the vertex focus
|
||||||
* or selects one or more nodes in a graph window
|
* or selects one or more nodes in a graph window
|
||||||
*
|
*
|
||||||
* @param listener the listener to be notified
|
* @param listener the listener to be notified
|
||||||
*/
|
*/
|
||||||
public void setGraphDisplayListener(GraphDisplayListener listener);
|
public void setGraphDisplayListener(GraphDisplayListener listener);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tells the graph display window to focus the vertex with the given id
|
* Tells the graph display window to focus the vertex with the given id
|
||||||
*
|
*
|
||||||
* @param vertex the vertex to focus
|
* @param vertex the vertex to focus
|
||||||
* @param eventTrigger Provides a hint to the GraphDisplay as to why we are updating the
|
* @param eventTrigger Provides a hint to the GraphDisplay as to why we are updating the
|
||||||
* graph location so that the GraphDisplay can decide if it should send out a notification via
|
* graph location so that the GraphDisplay can decide if it should send out a notification via
|
||||||
|
@ -116,14 +60,14 @@ public interface GraphDisplay {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the currently focused vertex or null if no vertex is focused
|
* Returns the currently focused vertex or null if no vertex is focused
|
||||||
*
|
*
|
||||||
* @return the currently focused vertex or null if no vertex is focused
|
* @return the currently focused vertex or null if no vertex is focused
|
||||||
*/
|
*/
|
||||||
public AttributedVertex getFocusedVertex();
|
public AttributedVertex getFocusedVertex();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tells the graph display window to select the vertices with the given ids
|
* Tells the graph display window to select the vertices with the given ids
|
||||||
*
|
*
|
||||||
* @param vertexSet the set of vertices to select
|
* @param vertexSet the set of vertices to select
|
||||||
* @param eventTrigger Provides a hint to the GraphDisplay as to why we are updating the
|
* @param eventTrigger Provides a hint to the GraphDisplay as to why we are updating the
|
||||||
* graph location so that the GraphDisplay can decide if it should send out a notification via
|
* graph location so that the GraphDisplay can decide if it should send out a notification via
|
||||||
|
@ -136,7 +80,7 @@ public interface GraphDisplay {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a set of vertex ids for all the currently selected vertices
|
* Returns a set of vertex ids for all the currently selected vertices
|
||||||
*
|
*
|
||||||
* @return a set of vertex ids for all the currently selected vertices
|
* @return a set of vertex ids for all the currently selected vertices
|
||||||
*/
|
*/
|
||||||
public Set<AttributedVertex> getSelectedVertices();
|
public Set<AttributedVertex> getSelectedVertices();
|
||||||
|
@ -148,7 +92,7 @@ public interface GraphDisplay {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the graph to be displayed or consumed by this graph display
|
* Sets the graph to be displayed or consumed by this graph display
|
||||||
*
|
*
|
||||||
* @param graph the graph to display or consume
|
* @param graph the graph to display or consume
|
||||||
* @param title a title for the graph
|
* @param title a title for the graph
|
||||||
* @param monitor a {@link TaskMonitor} which can be used to cancel the graphing operation
|
* @param monitor a {@link TaskMonitor} which can be used to cancel the graphing operation
|
||||||
|
@ -156,6 +100,7 @@ public interface GraphDisplay {
|
||||||
* @throws CancelledException thrown if the graphing operation was cancelled
|
* @throws CancelledException thrown if the graphing operation was cancelled
|
||||||
* @deprecated You should now use the form that takes in a {@link GraphDisplayOptions}
|
* @deprecated You should now use the form that takes in a {@link GraphDisplayOptions}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public default void setGraph(AttributedGraph graph, String title, boolean append,
|
public default void setGraph(AttributedGraph graph, String title, boolean append,
|
||||||
TaskMonitor monitor) throws CancelledException {
|
TaskMonitor monitor) throws CancelledException {
|
||||||
setGraph(graph, new GraphDisplayOptions(graph.getGraphType()), title, append, monitor);
|
setGraph(graph, new GraphDisplayOptions(graph.getGraphType()), title, append, monitor);
|
||||||
|
@ -163,7 +108,7 @@ public interface GraphDisplay {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the graph to be displayed or consumed by this graph display
|
* Sets the graph to be displayed or consumed by this graph display
|
||||||
*
|
*
|
||||||
* @param graph the graph to display or consume
|
* @param graph the graph to display or consume
|
||||||
* @param options {@link GraphDisplayOptions} for configuring how the display will
|
* @param options {@link GraphDisplayOptions} for configuring how the display will
|
||||||
* render vertices and edges based on there vertex type and edge type respectively.
|
* render vertices and edges based on there vertex type and edge type respectively.
|
||||||
|
@ -182,7 +127,7 @@ public interface GraphDisplay {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates a vertex to a new name
|
* Updates a vertex to a new name
|
||||||
*
|
*
|
||||||
* @param vertex the vertex to rename
|
* @param vertex the vertex to rename
|
||||||
* @param newName the new name for the vertex
|
* @param newName the new name for the vertex
|
||||||
*/
|
*/
|
||||||
|
@ -190,7 +135,7 @@ public interface GraphDisplay {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the title of the current graph
|
* Returns the title of the current graph
|
||||||
*
|
*
|
||||||
* @return the title of the current graph
|
* @return the title of the current graph
|
||||||
*/
|
*/
|
||||||
public String getGraphTitle();
|
public String getGraphTitle();
|
||||||
|
@ -198,7 +143,7 @@ public interface GraphDisplay {
|
||||||
/**
|
/**
|
||||||
* Adds the action to the graph display. Not all GraphDisplays support adding custom
|
* Adds the action to the graph display. Not all GraphDisplays support adding custom
|
||||||
* actions, so this may have no effect.
|
* actions, so this may have no effect.
|
||||||
*
|
*
|
||||||
* @param action the action to add
|
* @param action the action to add
|
||||||
*/
|
*/
|
||||||
public void addAction(DockingActionIf action);
|
public void addAction(DockingActionIf action);
|
||||||
|
|
|
@ -21,14 +21,10 @@ import java.util.Set;
|
||||||
* Interface for being notified when the user interacts with a visual graph display
|
* Interface for being notified when the user interacts with a visual graph display
|
||||||
*/
|
*/
|
||||||
public interface GraphDisplayListener {
|
public interface GraphDisplayListener {
|
||||||
/**
|
|
||||||
* Notification that the graph window has been closed
|
|
||||||
*/
|
|
||||||
public void graphClosed();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notification that the set of selected vertices has changed
|
* Notification that the set of selected vertices has changed
|
||||||
*
|
*
|
||||||
* @param vertices the set of currently selected vertices
|
* @param vertices the set of currently selected vertices
|
||||||
*/
|
*/
|
||||||
public void selectionChanged(Set<AttributedVertex> vertices);
|
public void selectionChanged(Set<AttributedVertex> vertices);
|
||||||
|
@ -42,7 +38,7 @@ public interface GraphDisplayListener {
|
||||||
/**
|
/**
|
||||||
* Makes a new GraphDisplayListener of the same type as the specific
|
* Makes a new GraphDisplayListener of the same type as the specific
|
||||||
* instance of this GraphDisplayListener
|
* instance of this GraphDisplayListener
|
||||||
*
|
*
|
||||||
* @param graphDisplay the new {@link GraphDisplay} the new listener will support
|
* @param graphDisplay the new {@link GraphDisplay} the new listener will support
|
||||||
* @return A new instance of a GraphDisplayListener that is the same type as as the instance
|
* @return A new instance of a GraphDisplayListener that is the same type as as the instance
|
||||||
* on which it is called
|
* on which it is called
|
||||||
|
@ -50,7 +46,8 @@ public interface GraphDisplayListener {
|
||||||
public GraphDisplayListener cloneWith(GraphDisplay graphDisplay);
|
public GraphDisplayListener cloneWith(GraphDisplay graphDisplay);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tells the listener that it is no longer needed and it can release any listeners/resources
|
* Tells the listener that it is no longer needed and it can release any listeners/resources.
|
||||||
|
* This will be called when a {@link GraphDisplay} is disposed or if this listener is replaced.
|
||||||
*/
|
*/
|
||||||
public void dispose();
|
public void dispose();
|
||||||
|
|
||||||
|
|
|
@ -72,9 +72,8 @@ public class GraphActionsTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
assertTrue(display.getSelectedVertices().isEmpty());
|
assertTrue(display.getSelectedVertices().isEmpty());
|
||||||
|
|
||||||
DockingActionIf action = getAction(tool, "Select Vertex");
|
DockingActionIf action = getAction(tool, "Select Vertex");
|
||||||
VertexGraphActionContext context =
|
VertexGraphActionContext context = new VertexGraphActionContext(graphComponentProvider,
|
||||||
new VertexGraphActionContext(graphComponentProvider, graph, null, null,
|
graph, null, null, graph.getVertex("B"));
|
||||||
graph.getVertex("B"));
|
|
||||||
performAction(action, context, true);
|
performAction(action, context, true);
|
||||||
|
|
||||||
Set<AttributedVertex> selectedVertices = display.getSelectedVertices();
|
Set<AttributedVertex> selectedVertices = display.getSelectedVertices();
|
||||||
|
@ -102,9 +101,8 @@ public class GraphActionsTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
assertEquals(4, display.getSelectedVertices().size());
|
assertEquals(4, display.getSelectedVertices().size());
|
||||||
|
|
||||||
DockingActionIf action = getAction(tool, "Deselect Vertex");
|
DockingActionIf action = getAction(tool, "Deselect Vertex");
|
||||||
VertexGraphActionContext context =
|
VertexGraphActionContext context = new VertexGraphActionContext(graphComponentProvider,
|
||||||
new VertexGraphActionContext(graphComponentProvider, graph, null, null,
|
graph, null, null, graph.getVertex("B"));
|
||||||
graph.getVertex("B"));
|
|
||||||
performAction(action, context, true);
|
performAction(action, context, true);
|
||||||
|
|
||||||
Set<AttributedVertex> selected = display.getSelectedVertices();
|
Set<AttributedVertex> selected = display.getSelectedVertices();
|
||||||
|
@ -121,9 +119,8 @@ public class GraphActionsTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
assertTrue(display.getSelectedVertices().isEmpty());
|
assertTrue(display.getSelectedVertices().isEmpty());
|
||||||
|
|
||||||
DockingActionIf action = getAction(tool, "Select Edge");
|
DockingActionIf action = getAction(tool, "Select Edge");
|
||||||
EdgeGraphActionContext context =
|
EdgeGraphActionContext context = new EdgeGraphActionContext(graphComponentProvider, graph,
|
||||||
new EdgeGraphActionContext(graphComponentProvider, graph, null, null,
|
null, null, graph.getEdge(graph.getVertex("A"), graph.getVertex("B")));
|
||||||
graph.getEdge(graph.getVertex("A"), graph.getVertex("B")));
|
|
||||||
performAction(action, context, true);
|
performAction(action, context, true);
|
||||||
|
|
||||||
Set<AttributedVertex> selectedVerticeIds = display.getSelectedVertices();
|
Set<AttributedVertex> selectedVerticeIds = display.getSelectedVertices();
|
||||||
|
@ -135,9 +132,8 @@ public class GraphActionsTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
@Test
|
@Test
|
||||||
public void testDeSelectEdgeAction() {
|
public void testDeSelectEdgeAction() {
|
||||||
DockingActionIf action = getAction(tool, "Select Edge");
|
DockingActionIf action = getAction(tool, "Select Edge");
|
||||||
EdgeGraphActionContext context =
|
EdgeGraphActionContext context = new EdgeGraphActionContext(graphComponentProvider, graph,
|
||||||
new EdgeGraphActionContext(graphComponentProvider, graph, null, null,
|
null, null, graph.getEdge(graph.getVertex("A"), graph.getVertex("B")));
|
||||||
graph.getEdge(graph.getVertex("A"), graph.getVertex("B")));
|
|
||||||
performAction(action, context, true);
|
performAction(action, context, true);
|
||||||
|
|
||||||
Set<AttributedVertex> selectedVertices = display.getSelectedVertices();
|
Set<AttributedVertex> selectedVertices = display.getSelectedVertices();
|
||||||
|
@ -155,9 +151,8 @@ public class GraphActionsTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
public void testSelectEdgeSource() {
|
public void testSelectEdgeSource() {
|
||||||
setFocusedVertex(d);
|
setFocusedVertex(d);
|
||||||
DockingActionIf action = getAction(tool, "Edge Source");
|
DockingActionIf action = getAction(tool, "Edge Source");
|
||||||
EdgeGraphActionContext context =
|
EdgeGraphActionContext context = new EdgeGraphActionContext(graphComponentProvider, graph,
|
||||||
new EdgeGraphActionContext(graphComponentProvider, graph, null, null,
|
null, null, graph.getEdge(graph.getVertex("A"), graph.getVertex("B")));
|
||||||
graph.getEdge(graph.getVertex("A"), graph.getVertex("B")));
|
|
||||||
performAction(action, context, true);
|
performAction(action, context, true);
|
||||||
|
|
||||||
assertEquals(a, display.getFocusedVertex());
|
assertEquals(a, display.getFocusedVertex());
|
||||||
|
@ -167,9 +162,8 @@ public class GraphActionsTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
public void testSelectEdgeTarget() {
|
public void testSelectEdgeTarget() {
|
||||||
setFocusedVertex(d);
|
setFocusedVertex(d);
|
||||||
DockingActionIf action = getAction(tool, "Edge Target");
|
DockingActionIf action = getAction(tool, "Edge Target");
|
||||||
EdgeGraphActionContext context =
|
EdgeGraphActionContext context = new EdgeGraphActionContext(graphComponentProvider, graph,
|
||||||
new EdgeGraphActionContext(graphComponentProvider, graph, null, null,
|
null, null, graph.getEdge(a, b));
|
||||||
graph.getEdge(a, b));
|
|
||||||
performAction(action, context, true);
|
performAction(action, context, true);
|
||||||
|
|
||||||
assertEquals(b, display.getFocusedVertex());
|
assertEquals(b, display.getFocusedVertex());
|
||||||
|
@ -523,11 +517,6 @@ public class GraphActionsTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
|
|
||||||
private class TestGraphDisplayListener implements GraphDisplayListener {
|
private class TestGraphDisplayListener implements GraphDisplayListener {
|
||||||
|
|
||||||
@Override
|
|
||||||
public void graphClosed() {
|
|
||||||
// do nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void selectionChanged(Set<AttributedVertex> vertices) {
|
public void selectionChanged(Set<AttributedVertex> vertices) {
|
||||||
graphSpy.setSelection(vertices);
|
graphSpy.setSelection(vertices);
|
||||||
|
@ -545,7 +534,7 @@ public class GraphActionsTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void dispose() {
|
public void dispose() {
|
||||||
// do nothing
|
// stub
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue