mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 18:29:37 +02:00
Miscellanious bug fixes and code clean up.
This commit is contained in:
parent
608e74f873
commit
b647c6cd5b
13 changed files with 405 additions and 346 deletions
|
@ -19,6 +19,7 @@ import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
import docking.widgets.EventTrigger;
|
||||||
import ghidra.app.cmd.label.AddLabelCmd;
|
import ghidra.app.cmd.label.AddLabelCmd;
|
||||||
import ghidra.app.cmd.label.RenameLabelCmd;
|
import ghidra.app.cmd.label.RenameLabelCmd;
|
||||||
import ghidra.app.events.*;
|
import ghidra.app.events.*;
|
||||||
|
@ -65,7 +66,7 @@ public abstract class AddressBasedGraphDisplayListener
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void locationChanged(String vertexId) {
|
public void locationFocusChanged(String vertexId) {
|
||||||
Address address = getAddressForVertexId(vertexId);
|
Address address = getAddressForVertexId(vertexId);
|
||||||
if (address != null) {
|
if (address != null) {
|
||||||
ProgramLocation location = new ProgramLocation(program, address);
|
ProgramLocation location = new ProgramLocation(program, address);
|
||||||
|
@ -101,7 +102,9 @@ public abstract class AddressBasedGraphDisplayListener
|
||||||
ProgramLocationPluginEvent ev = (ProgramLocationPluginEvent) event;
|
ProgramLocationPluginEvent ev = (ProgramLocationPluginEvent) event;
|
||||||
if (isMyProgram(ev.getProgram())) {
|
if (isMyProgram(ev.getProgram())) {
|
||||||
ProgramLocation location = ev.getLocation();
|
ProgramLocation location = ev.getLocation();
|
||||||
graphDisplay.setLocation(getVertexIdForAddress(location.getAddress()));
|
String id = getVertexIdForAddress(location.getAddress());
|
||||||
|
// update graph location, but tell it not to send out event
|
||||||
|
graphDisplay.setLocationFocus(id, EventTrigger.INTERNAL_ONLY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (event instanceof ProgramSelectionPluginEvent) {
|
else if (event instanceof ProgramSelectionPluginEvent) {
|
||||||
|
@ -110,7 +113,8 @@ public abstract class AddressBasedGraphDisplayListener
|
||||||
ProgramSelection selection = ev.getSelection();
|
ProgramSelection selection = ev.getSelection();
|
||||||
List<String> selectedVertices = getVertices(selection);
|
List<String> selectedVertices = getVertices(selection);
|
||||||
if (selectedVertices != null) {
|
if (selectedVertices != null) {
|
||||||
graphDisplay.selectVertices(selectedVertices);
|
// since we are responding to an event, tell the GraphDisplay not to send event
|
||||||
|
graphDisplay.selectVertices(selectedVertices, EventTrigger.INTERNAL_ONLY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ package ghidra.app.plugin.core.decompile.actions;
|
||||||
|
|
||||||
import static ghidra.app.plugin.core.decompile.actions.ASTGraphTask.GraphType.*;
|
import static ghidra.app.plugin.core.decompile.actions.ASTGraphTask.GraphType.*;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import ghidra.app.plugin.core.decompile.actions.ASTGraphTask.GraphType;
|
import ghidra.app.plugin.core.decompile.actions.ASTGraphTask.GraphType;
|
||||||
|
@ -26,6 +27,7 @@ import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.pcode.HighFunction;
|
import ghidra.program.model.pcode.HighFunction;
|
||||||
import ghidra.program.model.pcode.PcodeBlockBasic;
|
import ghidra.program.model.pcode.PcodeBlockBasic;
|
||||||
import ghidra.service.graph.GraphDisplay;
|
import ghidra.service.graph.GraphDisplay;
|
||||||
|
import ghidra.util.exception.AssertException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Listener for when an AST graph's nodes are selected.
|
* Listener for when an AST graph's nodes are selected.
|
||||||
|
@ -43,8 +45,20 @@ public class ASTGraphDisplayListener extends AddressBasedGraphDisplayListener {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected List<String> getVertices(AddressSetView selection) {
|
protected List<String> getVertices(AddressSetView selection) {
|
||||||
|
if (graphType != CONTROL_FLOW_GRAPH) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
List<String> vertices = new ArrayList<>();
|
||||||
|
List<PcodeBlockBasic> blocks = hfunction.getBasicBlocks();
|
||||||
|
for (PcodeBlockBasic block : blocks) {
|
||||||
|
Address start = block.getStart();
|
||||||
|
Address stop = block.getStop();
|
||||||
|
if (selection.intersects(start, stop)) {
|
||||||
|
vertices.add(Integer.toString(block.getIndex()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return vertices;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected AddressSet getAddressSetForVertices(List<String> vertexIds) {
|
protected AddressSet getAddressSetForVertices(List<String> vertexIds) {
|
||||||
|
@ -53,7 +67,6 @@ public class ASTGraphDisplayListener extends AddressBasedGraphDisplayListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
AddressSet set = new AddressSet();
|
AddressSet set = new AddressSet();
|
||||||
Address location = null;
|
|
||||||
List<PcodeBlockBasic> blocks = hfunction.getBasicBlocks();
|
List<PcodeBlockBasic> blocks = hfunction.getBasicBlocks();
|
||||||
for (String vertixId : vertexIds) {
|
for (String vertixId : vertexIds) {
|
||||||
try {
|
try {
|
||||||
|
@ -61,9 +74,6 @@ public class ASTGraphDisplayListener extends AddressBasedGraphDisplayListener {
|
||||||
PcodeBlockBasic block = blocks.get(index);
|
PcodeBlockBasic block = blocks.get(index);
|
||||||
Address start = block.getStart();
|
Address start = block.getStart();
|
||||||
set.addRange(start, block.getStop());
|
set.addRange(start, block.getStop());
|
||||||
if (location == null || start.compareTo(location) < 0) {
|
|
||||||
location = start;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (NumberFormatException e) {
|
catch (NumberFormatException e) {
|
||||||
// continue
|
// continue
|
||||||
|
@ -90,7 +100,16 @@ public class ASTGraphDisplayListener extends AddressBasedGraphDisplayListener {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Address getAddressForVertexId(String vertexId) {
|
protected Address getAddressForVertexId(String vertexId) {
|
||||||
return null;
|
List<PcodeBlockBasic> blocks = hfunction.getBasicBlocks();
|
||||||
|
|
||||||
|
try {
|
||||||
|
int index = Integer.parseInt(vertexId);
|
||||||
|
PcodeBlockBasic block = blocks.get(index);
|
||||||
|
return block.getStart();
|
||||||
|
}
|
||||||
|
catch (NumberFormatException e) {
|
||||||
|
throw new AssertException("Bad vertex id, expected a number but got " + vertexId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ package ghidra.app.plugin.core.decompile.actions;
|
||||||
|
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
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.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
|
@ -122,7 +123,9 @@ public class ASTGraphTask extends Task {
|
||||||
display.setGraph(graph, description, false, monitor);
|
display.setGraph(graph, description, false, monitor);
|
||||||
// set the graph location
|
// set the graph location
|
||||||
if (location != null) {
|
if (location != null) {
|
||||||
display.setLocation(displayListener.getVertexIdForAddress(location));
|
String id = displayListener.getVertexIdForAddress(location);
|
||||||
|
// update graph location, but don't have it send out event
|
||||||
|
display.setLocationFocus(id, EventTrigger.INTERNAL_ONLY);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ import java.util.List;
|
||||||
|
|
||||||
import org.jgrapht.Graph;
|
import org.jgrapht.Graph;
|
||||||
|
|
||||||
|
import docking.widgets.EventTrigger;
|
||||||
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.Swing;
|
||||||
|
@ -56,12 +57,12 @@ class ExportAttributedGraphDisplay implements GraphDisplay {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void selectVertices(List<String> vertexList) {
|
public void selectVertices(List<String> vertexList, EventTrigger eventTrigger) {
|
||||||
// This display is not interactive, so N/A
|
// This display is not interactive, so N/A
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setLocation(String vertexID) {
|
public void setLocationFocus(String vertexID, EventTrigger eventTrigger) {
|
||||||
// This display is not interactive, so N/A
|
// This display is not interactive, so N/A
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,17 +23,15 @@ import org.jungrapht.visualization.MultiLayerTransformer;
|
||||||
import org.jungrapht.visualization.VisualizationViewer;
|
import org.jungrapht.visualization.VisualizationViewer;
|
||||||
|
|
||||||
import ghidra.graph.job.AbstractAnimatorJob;
|
import ghidra.graph.job.AbstractAnimatorJob;
|
||||||
import ghidra.service.graph.AttributedEdge;
|
|
||||||
import ghidra.service.graph.AttributedVertex;
|
|
||||||
|
|
||||||
public class CenterAnimation<V, E> extends AbstractAnimatorJob {
|
public class CenterAnimationJob extends AbstractAnimatorJob {
|
||||||
protected int duration = 1000;
|
protected int duration = 1000;
|
||||||
private final Point2D oldPoint;
|
private final Point2D oldPoint;
|
||||||
private final Point2D newPoint;
|
private final Point2D newPoint;
|
||||||
private final Point2D lastPoint = new Point2D.Double();
|
private final Point2D lastPoint = new Point2D.Double();
|
||||||
private final VisualizationViewer<V, E> viewer;
|
private final VisualizationViewer<?, ?> viewer;
|
||||||
|
|
||||||
public CenterAnimation(VisualizationViewer<V, E> viewer,
|
public CenterAnimationJob(VisualizationViewer<?, ?> viewer,
|
||||||
Point2D oldPoint, Point2D newPoint) {
|
Point2D oldPoint, Point2D newPoint) {
|
||||||
this.viewer = viewer;
|
this.viewer = viewer;
|
||||||
this.oldPoint = oldPoint;
|
this.oldPoint = oldPoint;
|
|
@ -15,89 +15,56 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.graph.visualization;
|
package ghidra.graph.visualization;
|
||||||
|
|
||||||
import docking.ActionContext;
|
import static org.jungrapht.visualization.MultiLayerTransformer.Layer.*;
|
||||||
import docking.action.ToggleDockingAction;
|
import static org.jungrapht.visualization.renderers.BiModalRenderer.*;
|
||||||
import docking.action.builder.ActionBuilder;
|
|
||||||
import docking.action.builder.MultiStateActionBuilder;
|
|
||||||
import docking.action.builder.ToggleActionBuilder;
|
|
||||||
import docking.menu.ActionState;
|
|
||||||
import ghidra.framework.plugintool.Plugin;
|
|
||||||
import ghidra.framework.plugintool.PluginTool;
|
|
||||||
import ghidra.graph.AttributeFilters;
|
|
||||||
import ghidra.graph.job.GraphJobRunner;
|
|
||||||
import ghidra.service.graph.AttributedEdge;
|
|
||||||
import ghidra.service.graph.AttributedGraph;
|
|
||||||
import ghidra.service.graph.AttributedVertex;
|
|
||||||
import ghidra.service.graph.DummyGraphDisplayListener;
|
|
||||||
import ghidra.service.graph.GraphDisplay;
|
|
||||||
import ghidra.service.graph.GraphDisplayListener;
|
|
||||||
import ghidra.util.Msg;
|
|
||||||
import ghidra.util.Swing;
|
|
||||||
import ghidra.util.exception.CancelledException;
|
|
||||||
import ghidra.util.task.TaskMonitor;
|
|
||||||
import org.jgrapht.Graph;
|
|
||||||
import org.jungrapht.visualization.RenderContext;
|
|
||||||
import org.jungrapht.visualization.SatelliteVisualizationViewer;
|
|
||||||
import org.jungrapht.visualization.VisualizationViewer;
|
|
||||||
import org.jungrapht.visualization.annotations.MultiSelectedVertexPaintable;
|
|
||||||
import org.jungrapht.visualization.annotations.SingleSelectedVertexPaintable;
|
|
||||||
import org.jungrapht.visualization.control.DefaultGraphMouse;
|
|
||||||
import org.jungrapht.visualization.control.DefaultLensGraphMouse;
|
|
||||||
import org.jungrapht.visualization.control.DefaultSatelliteGraphMouse;
|
|
||||||
import org.jungrapht.visualization.control.LensGraphMouse;
|
|
||||||
import org.jungrapht.visualization.control.LensMagnificationGraphMousePlugin;
|
|
||||||
import org.jungrapht.visualization.control.MultiSelectionStrategy;
|
|
||||||
import org.jungrapht.visualization.decorators.EdgeShape;
|
|
||||||
import org.jungrapht.visualization.decorators.EllipseShapeFunction;
|
|
||||||
import org.jungrapht.visualization.decorators.IconShapeFunction;
|
|
||||||
import org.jungrapht.visualization.layout.algorithms.LayoutAlgorithm;
|
|
||||||
import org.jungrapht.visualization.layout.algorithms.util.InitialDimensionFunction;
|
|
||||||
import org.jungrapht.visualization.layout.model.LayoutModel;
|
|
||||||
import org.jungrapht.visualization.layout.model.Point;
|
|
||||||
import org.jungrapht.visualization.renderers.JLabelVertexLabelRenderer;
|
|
||||||
import org.jungrapht.visualization.renderers.LightweightVertexRenderer;
|
|
||||||
import org.jungrapht.visualization.renderers.ModalRenderer;
|
|
||||||
import org.jungrapht.visualization.renderers.Renderer;
|
|
||||||
import org.jungrapht.visualization.selection.MutableSelectedState;
|
|
||||||
import org.jungrapht.visualization.selection.VertexEndpointsSelectedEdgeSelectedState;
|
|
||||||
import org.jungrapht.visualization.transform.Lens;
|
|
||||||
import org.jungrapht.visualization.transform.LensSupport;
|
|
||||||
import org.jungrapht.visualization.transform.MutableTransformer;
|
|
||||||
import org.jungrapht.visualization.transform.shape.MagnifyImageLensSupport;
|
|
||||||
import org.jungrapht.visualization.transform.shape.MagnifyShapeTransformer;
|
|
||||||
import org.jungrapht.visualization.util.RectangleUtils;
|
|
||||||
import resources.Icons;
|
|
||||||
|
|
||||||
import javax.swing.AbstractButton;
|
import java.awt.*;
|
||||||
import javax.swing.BorderFactory;
|
import java.awt.event.*;
|
||||||
import javax.swing.JComponent;
|
|
||||||
import javax.swing.JRadioButton;
|
|
||||||
import javax.swing.event.AncestorEvent;
|
|
||||||
import javax.swing.event.AncestorListener;
|
|
||||||
import java.awt.BasicStroke;
|
|
||||||
import java.awt.Color;
|
|
||||||
import java.awt.Component;
|
|
||||||
import java.awt.Dimension;
|
|
||||||
import java.awt.event.ComponentAdapter;
|
|
||||||
import java.awt.event.ComponentEvent;
|
|
||||||
import java.awt.event.ItemEvent;
|
|
||||||
import java.awt.geom.Point2D;
|
import java.awt.geom.Point2D;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static org.jungrapht.visualization.MultiLayerTransformer.Layer.VIEW;
|
import javax.swing.*;
|
||||||
import static org.jungrapht.visualization.renderers.BiModalRenderer.LIGHTWEIGHT;
|
import javax.swing.event.AncestorEvent;
|
||||||
|
import javax.swing.event.AncestorListener;
|
||||||
|
|
||||||
|
import org.jgrapht.Graph;
|
||||||
|
import org.jungrapht.visualization.*;
|
||||||
|
import org.jungrapht.visualization.annotations.MultiSelectedVertexPaintable;
|
||||||
|
import org.jungrapht.visualization.annotations.SingleSelectedVertexPaintable;
|
||||||
|
import org.jungrapht.visualization.control.*;
|
||||||
|
import org.jungrapht.visualization.decorators.*;
|
||||||
|
import org.jungrapht.visualization.layout.algorithms.LayoutAlgorithm;
|
||||||
|
import org.jungrapht.visualization.layout.algorithms.util.InitialDimensionFunction;
|
||||||
|
import org.jungrapht.visualization.layout.model.LayoutModel;
|
||||||
|
import org.jungrapht.visualization.layout.model.Point;
|
||||||
|
import org.jungrapht.visualization.renderers.*;
|
||||||
|
import org.jungrapht.visualization.renderers.Renderer;
|
||||||
|
import org.jungrapht.visualization.selection.MutableSelectedState;
|
||||||
|
import org.jungrapht.visualization.selection.VertexEndpointsSelectedEdgeSelectedState;
|
||||||
|
import org.jungrapht.visualization.transform.*;
|
||||||
|
import org.jungrapht.visualization.transform.shape.MagnifyImageLensSupport;
|
||||||
|
import org.jungrapht.visualization.transform.shape.MagnifyShapeTransformer;
|
||||||
|
import org.jungrapht.visualization.util.RectangleUtils;
|
||||||
|
|
||||||
|
import docking.ActionContext;
|
||||||
|
import docking.action.ToggleDockingAction;
|
||||||
|
import docking.action.builder.*;
|
||||||
|
import docking.menu.ActionState;
|
||||||
|
import docking.widgets.EventTrigger;
|
||||||
|
import ghidra.framework.plugintool.Plugin;
|
||||||
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
|
import ghidra.graph.AttributeFilters;
|
||||||
|
import ghidra.graph.job.GraphJobRunner;
|
||||||
|
import ghidra.service.graph.*;
|
||||||
|
import ghidra.util.Msg;
|
||||||
|
import ghidra.util.Swing;
|
||||||
|
import ghidra.util.exception.CancelledException;
|
||||||
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
import resources.Icons;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delegates to a {@link VisualizationViewer} to draw a graph visualization
|
* Delegates to a {@link VisualizationViewer} to draw a graph visualization
|
||||||
|
@ -145,15 +112,16 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
private final DefaultGraphDisplayComponentProvider componentProvider;
|
private final DefaultGraphDisplayComponentProvider componentProvider;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* whether to scroll the visualization in order to center the selected vertex
|
* whether to ensure the focused vertex is visible, scrolling if necessary
|
||||||
* (or the centroid of the selected vertices)
|
* the visualization in order to center the selected vertex
|
||||||
|
* or the center of the set of selected vertices
|
||||||
*/
|
*/
|
||||||
private boolean enableScrollToSelection = false;
|
private boolean ensureVertexIsVisible = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* allows selection of various {@link LayoutAlgorithm} ('arrangements')
|
* allows selection of various {@link LayoutAlgorithm} ('arrangements')
|
||||||
*/
|
*/
|
||||||
private final LayoutTransitionManager<AttributedVertex, AttributedEdge> layoutTransitionManager;
|
private final LayoutTransitionManager layoutTransitionManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* provides graph displays for supplied graphs
|
* provides graph displays for supplied graphs
|
||||||
|
@ -164,9 +132,9 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
*/
|
*/
|
||||||
private LayoutWorkingDialog layoutWorkingDialog;
|
private LayoutWorkingDialog layoutWorkingDialog;
|
||||||
/**
|
/**
|
||||||
* the vertex that has been nominated to be 'located' in the graph display and listing
|
* the vertex that has been nominated to be 'focused' in the graph display and listing
|
||||||
*/
|
*/
|
||||||
private AttributedVertex locatedVertex;
|
private AttributedVertex focusedVertex;
|
||||||
private final GraphJobRunner jobRunner = new GraphJobRunner();
|
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
|
* a satellite view that shows in the lower left corner as a birds-eye view of the graph display
|
||||||
|
@ -208,10 +176,12 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
attributedGraph.addEdge(source, target, e);
|
attributedGraph.addEdge(source, target, e);
|
||||||
});
|
});
|
||||||
displaySubGraph(attributedGraph);
|
displaySubGraph(attributedGraph);
|
||||||
} catch (CancelledException e) {
|
}
|
||||||
|
catch (CancelledException e) {
|
||||||
// noop
|
// noop
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
private SwitchableSelectionItemListener switchableSelectionListener;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create the initial display, the graph-less visualization viewer, and its controls
|
* Create the initial display, the graph-less visualization viewer, and its controls
|
||||||
|
@ -229,7 +199,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
componentProvider.addToTool();
|
componentProvider.addToTool();
|
||||||
satelliteViewer = createSatelliteViewer(viewer);
|
satelliteViewer = createSatelliteViewer(viewer);
|
||||||
layoutTransitionManager =
|
layoutTransitionManager =
|
||||||
new LayoutTransitionManager<>(viewer, this::isRoot);
|
new LayoutTransitionManager(viewer, this::isRoot);
|
||||||
|
|
||||||
viewer.getComponent().addComponentListener(new ComponentAdapter() {
|
viewer.getComponent().addComponentListener(new ComponentAdapter() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -244,7 +214,8 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
});
|
});
|
||||||
|
|
||||||
viewer.setInitialDimensionFunction(InitialDimensionFunction
|
viewer.setInitialDimensionFunction(InitialDimensionFunction
|
||||||
.builder(viewer.getRenderContext().getVertexBoundsFunction()).build());
|
.builder(viewer.getRenderContext().getVertexBoundsFunction())
|
||||||
|
.build());
|
||||||
|
|
||||||
createActions();
|
createActions();
|
||||||
connectSelectionStateListeners();
|
connectSelectionStateListeners();
|
||||||
|
@ -286,22 +257,23 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* create the highlighters ({@code Paintable}s to show which vertices have been selected or located
|
* create the highlighters ({@code Paintable}s to show which vertices have been selected or focused)
|
||||||
*/
|
*/
|
||||||
private void buildHighlighers() {
|
private void buildHighlighers() {
|
||||||
// for highlighting of multiple selected vertices
|
// for highlighting of multiple selected vertices
|
||||||
MultiSelectedVertexPaintable<AttributedVertex, AttributedEdge> multiSelectedVertexPaintable = MultiSelectedVertexPaintable.builder(viewer)
|
MultiSelectedVertexPaintable<AttributedVertex, AttributedEdge> multiSelectedVertexPaintable =
|
||||||
|
MultiSelectedVertexPaintable.builder(viewer)
|
||||||
.selectionStrokeMin(4.f)
|
.selectionStrokeMin(4.f)
|
||||||
.selectionPaint(Color.red)
|
.selectionPaint(Color.red)
|
||||||
.useBounds(false)
|
.useBounds(false)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
|
||||||
// manages highlight painting of a single selected vertex
|
// manages highlight painting of a single selected vertex
|
||||||
SingleSelectedVertexPaintable<AttributedVertex, AttributedEdge> singleSelectedVertexPaintable = SingleSelectedVertexPaintable.builder(viewer)
|
SingleSelectedVertexPaintable<AttributedVertex, AttributedEdge> singleSelectedVertexPaintable =
|
||||||
|
SingleSelectedVertexPaintable.builder(viewer)
|
||||||
.selectionStrokeMin(4.f)
|
.selectionStrokeMin(4.f)
|
||||||
.selectionPaint(Color.red)
|
.selectionPaint(Color.red)
|
||||||
.selectedVertexFunction(vs -> this.locatedVertex)
|
.selectedVertexFunction(vs -> this.focusedVertex)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
// draws the selection highlights
|
// draws the selection highlights
|
||||||
|
@ -320,23 +292,24 @@ 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", pluginName)
|
new ToggleActionBuilder("Scroll To Selection", pluginName)
|
||||||
.toolBarIcon(Icons.NAVIGATE_ON_INCOMING_EVENT_ICON)
|
.toolBarIcon(Icons.NAVIGATE_ON_INCOMING_EVENT_ICON)
|
||||||
.description("Scroll display to center the 'Located' vertex")
|
.description("Ensure that the 'focused' vertex is visible")
|
||||||
.selected(false)
|
.selected(true)
|
||||||
.onAction(context -> enableScrollToSelection =
|
.onAction(context -> ensureVertexIsVisible =
|
||||||
((AbstractButton) context.getSourceObject()).isSelected())
|
((AbstractButton) context.getSourceObject()).isSelected())
|
||||||
.buildAndInstallLocal(componentProvider);
|
.buildAndInstallLocal(componentProvider);
|
||||||
|
|
||||||
|
this.ensureVertexIsVisible = true; // since we intialized action to selected
|
||||||
|
|
||||||
// create a toggle for enabling 'free-form' selection: selection is
|
// create a toggle for enabling 'free-form' selection: selection is
|
||||||
// inside of a traced shape instead of a rectangle
|
// inside of a traced shape instead of a rectangle
|
||||||
new ToggleActionBuilder("Free-Form Selection", pluginName)
|
new ToggleActionBuilder("Free-Form Selection", pluginName)
|
||||||
.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 ->
|
.onAction(context -> freeFormSelection =
|
||||||
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", pluginName).description("Show Satellite View")
|
new ToggleActionBuilder("SatelliteView", pluginName).description("Show Satellite View")
|
||||||
.toolBarIcon(DefaultDisplayGraphIcons.SATELLITE_VIEW_ICON)
|
.toolBarIcon(DefaultDisplayGraphIcons.SATELLITE_VIEW_ICON)
|
||||||
|
@ -356,11 +329,10 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
.description("Show View Magnifier")
|
.description("Show View Magnifier")
|
||||||
.toolBarIcon(DefaultDisplayGraphIcons.VIEW_MAGNIFIER_ICON)
|
.toolBarIcon(DefaultDisplayGraphIcons.VIEW_MAGNIFIER_ICON)
|
||||||
.onAction(context -> magnifyViewSupport.activate(
|
.onAction(context -> magnifyViewSupport.activate(
|
||||||
((AbstractButton) context.getSourceObject()).isSelected()
|
((AbstractButton) context.getSourceObject()).isSelected()))
|
||||||
))
|
|
||||||
.build();
|
.build();
|
||||||
magnifyViewSupport.addItemListener(itemEvent ->
|
magnifyViewSupport.addItemListener(
|
||||||
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
|
||||||
|
@ -379,16 +351,18 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
.buildAndInstallLocal(componentProvider);
|
.buildAndInstallLocal(componentProvider);
|
||||||
|
|
||||||
// show a 'busy' dialog while the layout algorithm is computing vertex locations
|
// show a 'busy' dialog while the layout algorithm is computing vertex locations
|
||||||
viewer.getVisualizationModel().getLayoutModel()
|
viewer.getVisualizationModel()
|
||||||
.getLayoutStateChangeSupport().addLayoutStateChangeListener(
|
.getLayoutModel()
|
||||||
|
.getLayoutStateChangeSupport()
|
||||||
|
.addLayoutStateChangeListener(
|
||||||
evt -> {
|
evt -> {
|
||||||
if (evt.active) {
|
if (evt.active) {
|
||||||
Swing.runLater(this::showLayoutWorking);
|
Swing.runLater(this::showLayoutWorking);
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
Swing.runLater(this::hideLayoutWorking);
|
Swing.runLater(this::hideLayoutWorking);
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -465,14 +439,10 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
viewer.repaint();
|
viewer.repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private void displaySubGraph(Graph<AttributedVertex, AttributedEdge> subGraph)
|
||||||
* from the supplied {@link Graph}, create a new GraphDisplay in a new window or tab
|
throws CancelledException {
|
||||||
* @param subGraph
|
|
||||||
* @throws CancelledException
|
|
||||||
*/
|
|
||||||
private void displaySubGraph(Graph<AttributedVertex, AttributedEdge> subGraph) throws CancelledException {
|
|
||||||
GraphDisplay graphDisplay = graphDisplayProvider.getGraphDisplay(false, TaskMonitor.DUMMY);
|
GraphDisplay graphDisplay = graphDisplayProvider.getGraphDisplay(false, TaskMonitor.DUMMY);
|
||||||
graphDisplay.setGraph((AttributedGraph)subGraph, "SubGraph", false, TaskMonitor.DUMMY);
|
graphDisplay.setGraph((AttributedGraph) subGraph, "SubGraph", false, TaskMonitor.DUMMY);
|
||||||
graphDisplay.setGraphDisplayListener(listener);
|
graphDisplay.setGraphDisplayListener(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -486,27 +456,27 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
Dimension viewerSize = parentViewer.getSize();
|
Dimension viewerSize = parentViewer.getSize();
|
||||||
Dimension satelliteSize = new Dimension(
|
Dimension satelliteSize = new Dimension(
|
||||||
viewerSize.width / 4, viewerSize.height / 4);
|
viewerSize.width / 4, viewerSize.height / 4);
|
||||||
final SatelliteVisualizationViewer<AttributedVertex, AttributedEdge> satelliteViewer =
|
final SatelliteVisualizationViewer<AttributedVertex, AttributedEdge> satellite =
|
||||||
SatelliteVisualizationViewer.builder(parentViewer)
|
SatelliteVisualizationViewer.builder(parentViewer)
|
||||||
.viewSize(satelliteSize)
|
.viewSize(satelliteSize)
|
||||||
.build();
|
.build();
|
||||||
satelliteViewer.setGraphMouse(new DefaultSatelliteGraphMouse());
|
satellite.setGraphMouse(new DefaultSatelliteGraphMouse());
|
||||||
satelliteViewer.getRenderContext().setEdgeDrawPaintFunction(Colors::getColor);
|
satellite.getRenderContext().setEdgeDrawPaintFunction(Colors::getColor);
|
||||||
satelliteViewer.getRenderContext()
|
satellite.getRenderContext()
|
||||||
.setEdgeStrokeFunction(ProgramGraphFunctions::getEdgeStroke);
|
.setEdgeStrokeFunction(ProgramGraphFunctions::getEdgeStroke);
|
||||||
satelliteViewer.getRenderContext().setVertexFillPaintFunction(Colors::getColor);
|
satellite.getRenderContext().setVertexFillPaintFunction(Colors::getColor);
|
||||||
satelliteViewer.scaleToLayout();
|
satellite.scaleToLayout();
|
||||||
satelliteViewer.getRenderContext().setVertexLabelFunction(n -> null);
|
satellite.getRenderContext().setVertexLabelFunction(n -> null);
|
||||||
satelliteViewer.getComponent().setBorder(BorderFactory.createEtchedBorder());
|
satellite.getComponent().setBorder(BorderFactory.createEtchedBorder());
|
||||||
parentViewer.getComponent().addComponentListener(new ComponentAdapter() {
|
parentViewer.getComponent().addComponentListener(new ComponentAdapter() {
|
||||||
@Override
|
@Override
|
||||||
public void componentResized(ComponentEvent evt) {
|
public void componentResized(ComponentEvent evt) {
|
||||||
Dimension size = evt.getComponent().getSize();
|
Dimension size = evt.getComponent().getSize();
|
||||||
Dimension quarterSize = new Dimension(size.width / 4, size.height / 4);
|
Dimension quarterSize = new Dimension(size.width / 4, size.height / 4);
|
||||||
satelliteViewer.getComponent().setSize(quarterSize);
|
satellite.getComponent().setSize(quarterSize);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return satelliteViewer;
|
return satellite;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -532,10 +502,10 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
}
|
}
|
||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
DefaultGraphMouse<AttributedVertex, AttributedEdge> graphMouse =
|
DefaultGraphMouse<AttributedVertex, AttributedEdge> graphMouse =
|
||||||
GhidraGraphMouse.<AttributedVertex, AttributedEdge>builder()
|
GhidraGraphMouse.<AttributedVertex, AttributedEdge> builder()
|
||||||
.viewer(viewer)
|
.viewer(viewer)
|
||||||
.subgraphConsumer(subgraphConsumer)
|
.subgraphConsumer(subgraphConsumer)
|
||||||
.locatedVertexConsumer(this::setLocatedVertex)
|
.locatedVertexConsumer(this::setFocusedVertex)
|
||||||
.graphDisplayListener(listener)
|
.graphDisplayListener(listener)
|
||||||
.vertexIdFunction(AttributedVertex::getId)
|
.vertexIdFunction(AttributedVertex::getId)
|
||||||
.vertexNameFunction(AttributedVertex::getName)
|
.vertexNameFunction(AttributedVertex::getName)
|
||||||
|
@ -547,54 +517,55 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
* connect the selection state to to the visualization
|
* connect the selection state to to the visualization
|
||||||
*/
|
*/
|
||||||
private void connectSelectionStateListeners() {
|
private void connectSelectionStateListeners() {
|
||||||
viewer.getSelectedVertexState().addItemListener(e -> Swing.runLater(() -> {
|
switchableSelectionListener = new SwitchableSelectionItemListener();
|
||||||
// there was a change in the set of selected vertices.
|
viewer.getSelectedVertexState().addItemListener(switchableSelectionListener);
|
||||||
// if the locatedVertex is null, set it from one of the selected
|
}
|
||||||
// vertices
|
|
||||||
if (e.getStateChange() == ItemEvent.SELECTED) {
|
|
||||||
Collection<AttributedVertex> selectedVertices = getVertices(e.getItem());
|
|
||||||
List<String> selectedVertexIds = toVertexIds(selectedVertices);
|
|
||||||
notifySelectionChanged(selectedVertexIds);
|
|
||||||
|
|
||||||
if (selectedVertices.size() == 1) {
|
protected void setFocusedVertex(AttributedVertex vertex) {
|
||||||
// if only one vertex was selected, make it the locatedVertex
|
setFocusedVertex(vertex, EventTrigger.API_CALL);
|
||||||
setLocatedVertex(selectedVertices.stream().findFirst().get());
|
|
||||||
} else if (this.locatedVertex == null) {
|
|
||||||
// if there is currently no locatedVertex, attempt to get
|
|
||||||
// one from the selectedVertices
|
|
||||||
setLocatedVertex(selectedVertices.stream().findFirst().orElse(null));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void setFocusedVertex(AttributedVertex vertex, EventTrigger eventTrigger) {
|
||||||
|
boolean changed = this.focusedVertex != vertex;
|
||||||
|
this.focusedVertex = vertex;
|
||||||
|
if (focusedVertex != null) {
|
||||||
|
if (changed && eventTrigger != EventTrigger.INTERNAL_ONLY) {
|
||||||
|
notifyLocationFocusChanged(focusedVertex.getId());
|
||||||
}
|
}
|
||||||
else if (e.getStateChange() == ItemEvent.DESELECTED) {
|
// make sure the vertex is visible, even if the vertex has not changed
|
||||||
notifySelectionChanged(Collections.emptyList());
|
scrollToSelected(focusedVertex);
|
||||||
}
|
|
||||||
viewer.repaint();
|
viewer.repaint();
|
||||||
}));
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* set the vertex that has been nominated to be 'located'
|
* determines whether the passed layout coordinates are visible in the display
|
||||||
* @param vertex the lucky vertex
|
* @param x of interest (layout coordinates)
|
||||||
|
* @param y of interest (layout coordinates)
|
||||||
|
* @return {@code true} if the coordinates are visible in the display view
|
||||||
*/
|
*/
|
||||||
protected void setLocatedVertex(AttributedVertex vertex) {
|
private boolean isVisible(double x, double y) {
|
||||||
boolean changed = this.locatedVertex != vertex;
|
if (viewer.getComponent().isVisible() && !viewer.getBounds().isEmpty()) {
|
||||||
this.locatedVertex = vertex;
|
// project the view bounds into the layout coordinate system, test for containing the coordinates
|
||||||
if (locatedVertex != null && changed) {
|
return viewer.getRenderContext()
|
||||||
notifyLocationChanged(locatedVertex.getId());
|
.getMultiLayerTransformer()
|
||||||
scrollToSelected(locatedVertex);
|
.inverseTransform(viewer.getBounds())
|
||||||
viewer.repaint();
|
.getBounds()
|
||||||
|
.contains(x, y);
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* transform the supplied {@code AttributedVertex} Set members to a List of their ids
|
* transform the supplied {@code AttributedVertex}s to a List of their ids
|
||||||
* @param selectedVertices
|
* @param selectedVertices the collections of vertices.
|
||||||
* @return
|
* @return a list of vertex ids
|
||||||
*/
|
*/
|
||||||
private List<String> toVertexIds(Collection<AttributedVertex> selectedVertices) {
|
private List<String> toVertexIds(Collection<AttributedVertex> selectedVertices) {
|
||||||
return selectedVertices.stream().map(AttributedVertex::getId).collect(Collectors.toList());
|
return selectedVertices.stream().map(AttributedVertex::getId).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
private Collection<AttributedVertex> getVertices(Object item) {
|
private Collection<AttributedVertex> getVertices(Object item) {
|
||||||
if (item instanceof Collection) {
|
if (item instanceof Collection) {
|
||||||
return (Collection<AttributedVertex>) item;
|
return (Collection<AttributedVertex>) item;
|
||||||
|
@ -606,28 +577,30 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* fire an event to say the selected vertices changed
|
* fire an event to notify the selected vertices changed
|
||||||
* @param vertexIds
|
* @param vertexIds the list of vertexes
|
||||||
*/
|
*/
|
||||||
private void notifySelectionChanged(List<String> vertexIds) {
|
private void notifySelectionChanged(List<String> vertexIds) {
|
||||||
Swing.runLater(() -> listener.selectionChanged(vertexIds));
|
Swing.runLater(() -> listener.selectionChanged(vertexIds));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* fire and event to say the located vertex changed
|
* fire and event to say the focused vertex changed
|
||||||
* @param vertexId
|
* @param vertexId the id of the focused vertex
|
||||||
*/
|
*/
|
||||||
private void notifyLocationChanged(String vertexId) {
|
private void notifyLocationFocusChanged(String vertexId) {
|
||||||
Swing.runLater(() -> listener.locationChanged(vertexId));
|
Swing.runLater(() -> listener.locationFocusChanged(vertexId));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Pass the supplied list of vertex id's to the underlying visualization to cause them to be 'selected' visually
|
|
||||||
* @param vertexIdList the vertex ids to select
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void selectVertices(List<String> vertexIdList) {
|
public void selectVertices(List<String> vertexIdList, EventTrigger eventTrigger) {
|
||||||
MutableSelectedState<AttributedVertex> nodeSelectedState = viewer.getSelectedVertexState();
|
// if we are not to fire events, turn off the selection listener we provided to the
|
||||||
|
// graphing library.
|
||||||
|
switchableSelectionListener.setEnabled(eventTrigger != EventTrigger.INTERNAL_ONLY);
|
||||||
|
|
||||||
|
try {
|
||||||
|
MutableSelectedState<AttributedVertex> nodeSelectedState =
|
||||||
|
viewer.getSelectedVertexState();
|
||||||
Set<AttributedVertex> selected = getVertices(vertexIdList);
|
Set<AttributedVertex> selected = getVertices(vertexIdList);
|
||||||
if (vertexIdList.isEmpty()) {
|
if (vertexIdList.isEmpty()) {
|
||||||
nodeSelectedState.clear();
|
nodeSelectedState.clear();
|
||||||
|
@ -639,6 +612,11 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
}
|
}
|
||||||
viewer.repaint();
|
viewer.repaint();
|
||||||
}
|
}
|
||||||
|
finally {
|
||||||
|
// always turn on the selection listener
|
||||||
|
switchableSelectionListener.setEnabled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
@ -653,20 +631,14 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* for the supplied vertex id, find the {@code AttributedVertex} and translate
|
|
||||||
* the display to center it
|
|
||||||
* @param vertexID the id of the vertex to focus
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void setLocation(String vertexID) {
|
public void setLocationFocus(String vertexID, EventTrigger eventTrigger) {
|
||||||
Optional<AttributedVertex> located =
|
Optional<AttributedVertex> vertexToFocus =
|
||||||
graph.vertexSet().stream().filter(v -> vertexID.equals(v.getId())).findFirst();
|
graph.vertexSet().stream().filter(v -> vertexID.equals(v.getId())).findFirst();
|
||||||
log.fine("picking address:" + vertexID + " returned " + located);
|
log.fine("picking address:" + vertexID + " returned " + vertexToFocus);
|
||||||
viewer.repaint();
|
viewer.repaint();
|
||||||
located.ifPresent(v -> {
|
vertexToFocus.ifPresent(v -> {
|
||||||
setLocatedVertex(v);
|
setFocusedVertex(v, eventTrigger);
|
||||||
scrollToSelected(v);
|
|
||||||
});
|
});
|
||||||
viewer.repaint();
|
viewer.repaint();
|
||||||
}
|
}
|
||||||
|
@ -854,23 +826,24 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
* @param vertices the vertices to center
|
* @param vertices the vertices to center
|
||||||
*/
|
*/
|
||||||
void scrollToSelected(Collection<AttributedVertex> vertices) {
|
void scrollToSelected(Collection<AttributedVertex> vertices) {
|
||||||
|
if (ensureVertexIsVisible) {
|
||||||
if (!enableScrollToSelection) {
|
jobRunner.finishAllJobs();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Point2D newCenter = getPointToCenter(vertices);
|
Point2D newCenter = getPointToCenter(vertices);
|
||||||
|
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 CenterAnimation<>(viewer, existingCenter, newCenter));
|
jobRunner.schedule(new CenterAnimationJob(viewer, existingCenter, newCenter));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**w
|
/**
|
||||||
* scroll the visualization to center the passed vertex
|
* scroll the visualization to center the passed vertex
|
||||||
* @param vertex the vertex to center
|
* @param vertex the vertex to center
|
||||||
*/
|
*/
|
||||||
void scrollToSelected(AttributedVertex vertex) {
|
private void scrollToSelected(AttributedVertex vertex) {
|
||||||
List<AttributedVertex> vertices =
|
List<AttributedVertex> vertices =
|
||||||
vertex == null ? Collections.emptyList() : List.of(vertex);
|
vertex == null ? Collections.emptyList() : List.of(vertex);
|
||||||
scrollToSelected(vertices);
|
scrollToSelected(vertices);
|
||||||
|
@ -908,8 +881,10 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
@Override
|
@Override
|
||||||
public void updateVertexName(String id, String newName) {
|
public void updateVertexName(String id, String newName) {
|
||||||
// find the vertex, if present, change the name
|
// find the vertex, if present, change the name
|
||||||
Optional<AttributedVertex> optional = graph.vertexSet().stream()
|
Optional<AttributedVertex> optional = graph.vertexSet()
|
||||||
.filter(v -> v.getId().equals(id)).findFirst();
|
.stream()
|
||||||
|
.filter(v -> v.getId().equals(id))
|
||||||
|
.findFirst();
|
||||||
if (optional.isPresent()) {
|
if (optional.isPresent()) {
|
||||||
AttributedVertex vertex = optional.get();
|
AttributedVertex vertex = optional.get();
|
||||||
vertex.setName(newName);
|
vertex.setName(newName);
|
||||||
|
@ -930,13 +905,14 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* create and return a {@link VisualizationViewer} to display graphs
|
* create and return a {@link VisualizationViewer} to display graphs
|
||||||
* @return
|
* @return the new VisualizationViewer
|
||||||
*/
|
*/
|
||||||
public VisualizationViewer<AttributedVertex, AttributedEdge> createViewer() {
|
public VisualizationViewer<AttributedVertex, AttributedEdge> createViewer() {
|
||||||
final VisualizationViewer<AttributedVertex, AttributedEdge> vv =
|
final VisualizationViewer<AttributedVertex, AttributedEdge> vv =
|
||||||
VisualizationViewer.<AttributedVertex, AttributedEdge> builder()
|
VisualizationViewer.<AttributedVertex, AttributedEdge> builder()
|
||||||
.multiSelectionStrategySupplier(() -> freeFormSelection ?
|
.multiSelectionStrategySupplier(
|
||||||
MultiSelectionStrategy.arbitrary() : MultiSelectionStrategy.rectangular())
|
() -> freeFormSelection ? MultiSelectionStrategy.arbitrary()
|
||||||
|
: MultiSelectionStrategy.rectangular())
|
||||||
.viewSize(PREFERRED_VIEW_SIZE)
|
.viewSize(PREFERRED_VIEW_SIZE)
|
||||||
.layoutSize(PREFERRED_LAYOUT_SIZE)
|
.layoutSize(PREFERRED_LAYOUT_SIZE)
|
||||||
.build();
|
.build();
|
||||||
|
@ -955,12 +931,12 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void ancestorRemoved(AncestorEvent ancestorEvent) {
|
public void ancestorRemoved(AncestorEvent ancestorEvent) {
|
||||||
|
// do nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void ancestorMoved(AncestorEvent ancestorEvent) {
|
public void ancestorMoved(AncestorEvent ancestorEvent) {
|
||||||
|
// do nothing
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -981,7 +957,9 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
renderContext.setVertexIconFunction(iconCache::get);
|
renderContext.setVertexIconFunction(iconCache::get);
|
||||||
|
|
||||||
vv.setInitialDimensionFunction(InitialDimensionFunction
|
vv.setInitialDimensionFunction(InitialDimensionFunction
|
||||||
.builder(nodeImageShapeFunction.andThen(s -> RectangleUtils.convert(s.getBounds2D()))).build());
|
.builder(
|
||||||
|
nodeImageShapeFunction.andThen(s -> RectangleUtils.convert(s.getBounds2D())))
|
||||||
|
.build());
|
||||||
|
|
||||||
// the selectedEdgeState will be controlled by the vertices that are selected.
|
// the selectedEdgeState will be controlled by the vertices that are selected.
|
||||||
// if both endpoints of an edge are selected, select that edge.
|
// if both endpoints of an edge are selected, select that edge.
|
||||||
|
@ -1030,4 +1008,49 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
vv.setBackground(Color.WHITE);
|
vv.setBackground(Color.WHITE);
|
||||||
return vv;
|
return vv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Item listener for selection changes in the graph with the additional
|
||||||
|
* capability of being able to disable the listener without removing it.
|
||||||
|
*/
|
||||||
|
class SwitchableSelectionItemListener implements ItemListener {
|
||||||
|
boolean enabled = true;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void itemStateChanged(ItemEvent e) {
|
||||||
|
if (enabled) {
|
||||||
|
Swing.runLater(() -> run(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void run(ItemEvent e) {
|
||||||
|
// there was a change in the set of selected vertices.
|
||||||
|
// if the focused vertex is null, set it from one of the selected
|
||||||
|
// vertices
|
||||||
|
if (e.getStateChange() == ItemEvent.SELECTED) {
|
||||||
|
Collection<AttributedVertex> selectedVertices = getVertices(e.getItem());
|
||||||
|
List<String> selectedVertexIds = toVertexIds(selectedVertices);
|
||||||
|
notifySelectionChanged(selectedVertexIds);
|
||||||
|
|
||||||
|
if (selectedVertices.size() == 1) {
|
||||||
|
// if only one vertex was selected, make it the focused vertex
|
||||||
|
setFocusedVertex(selectedVertices.stream().findFirst().get());
|
||||||
|
}
|
||||||
|
else if (DefaultGraphDisplay.this.focusedVertex == null) {
|
||||||
|
// if there is currently no focused Vertex, attempt to get
|
||||||
|
// one from the selectedVertices
|
||||||
|
setFocusedVertex(selectedVertices.stream().findFirst().orElse(null));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (e.getStateChange() == ItemEvent.DESELECTED) {
|
||||||
|
notifySelectionChanged(Collections.emptyList());
|
||||||
|
}
|
||||||
|
viewer.repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setEnabled(boolean enabled) {
|
||||||
|
this.enabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,21 +15,14 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.graph.visualization;
|
package ghidra.graph.visualization;
|
||||||
|
|
||||||
import org.jungrapht.visualization.layout.algorithms.BalloonLayoutAlgorithm;
|
import java.util.function.Function;
|
||||||
import org.jungrapht.visualization.layout.algorithms.CircleLayoutAlgorithm;
|
|
||||||
import org.jungrapht.visualization.layout.algorithms.EiglspergerLayoutAlgorithm;
|
import org.jungrapht.visualization.layout.algorithms.*;
|
||||||
import org.jungrapht.visualization.layout.algorithms.FRLayoutAlgorithm;
|
|
||||||
import org.jungrapht.visualization.layout.algorithms.GEMLayoutAlgorithm;
|
|
||||||
import org.jungrapht.visualization.layout.algorithms.KKLayoutAlgorithm;
|
|
||||||
import org.jungrapht.visualization.layout.algorithms.LayoutAlgorithm;
|
|
||||||
import org.jungrapht.visualization.layout.algorithms.RadialTreeLayoutAlgorithm;
|
|
||||||
import org.jungrapht.visualization.layout.algorithms.TidierRadialTreeLayoutAlgorithm;
|
|
||||||
import org.jungrapht.visualization.layout.algorithms.TidierTreeLayoutAlgorithm;
|
|
||||||
import org.jungrapht.visualization.layout.algorithms.TreeLayoutAlgorithm;
|
|
||||||
import org.jungrapht.visualization.layout.algorithms.repulsion.BarnesHutFRRepulsion;
|
import org.jungrapht.visualization.layout.algorithms.repulsion.BarnesHutFRRepulsion;
|
||||||
import org.jungrapht.visualization.layout.algorithms.sugiyama.Layering;
|
import org.jungrapht.visualization.layout.algorithms.sugiyama.Layering;
|
||||||
|
|
||||||
import java.util.function.Function;
|
import ghidra.service.graph.AttributedEdge;
|
||||||
|
import ghidra.service.graph.AttributedVertex;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A central location to list and provide all layout algorithms, their names, and their builders
|
* A central location to list and provide all layout algorithms, their names, and their builders
|
||||||
|
@ -38,8 +31,8 @@ import java.util.function.Function;
|
||||||
* This class provides LayoutAlgorithm builders instead of LayoutAlgorithms because some LayoutAlgorithms
|
* This class provides LayoutAlgorithm builders instead of LayoutAlgorithms because some LayoutAlgorithms
|
||||||
* accumulate state information (so are used only one time).
|
* accumulate state information (so are used only one time).
|
||||||
*/
|
*/
|
||||||
class LayoutFunction<V, E>
|
class LayoutFunction
|
||||||
implements Function<String, LayoutAlgorithm.Builder<V, ?, ?>> {
|
implements Function<String, LayoutAlgorithm.Builder<AttributedVertex, ?, ?>> {
|
||||||
|
|
||||||
static final String KAMADA_KAWAI = "Force Balanced";
|
static final String KAMADA_KAWAI = "Force Balanced";
|
||||||
static final String FRUCTERMAN_REINGOLD = "Force Directed";
|
static final String FRUCTERMAN_REINGOLD = "Force Directed";
|
||||||
|
@ -64,51 +57,53 @@ class LayoutFunction<V, E>
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LayoutAlgorithm.Builder<V, ?, ?> apply(String name) {
|
public LayoutAlgorithm.Builder<AttributedVertex, ?, ?> apply(String name) {
|
||||||
switch(name) {
|
switch(name) {
|
||||||
case GEM:
|
case GEM:
|
||||||
return GEMLayoutAlgorithm.edgeAwareBuilder();
|
return GEMLayoutAlgorithm.edgeAwareBuilder();
|
||||||
case KAMADA_KAWAI:
|
case KAMADA_KAWAI:
|
||||||
return KKLayoutAlgorithm.<V> builder()
|
return KKLayoutAlgorithm.<AttributedVertex> builder()
|
||||||
.preRelaxDuration(1000);
|
.preRelaxDuration(1000);
|
||||||
case FRUCTERMAN_REINGOLD:
|
case FRUCTERMAN_REINGOLD:
|
||||||
return FRLayoutAlgorithm.<V> builder()
|
return FRLayoutAlgorithm.<AttributedVertex> builder()
|
||||||
.repulsionContractBuilder(BarnesHutFRRepulsion.builder());
|
.repulsionContractBuilder(BarnesHutFRRepulsion.builder());
|
||||||
case CIRCLE_MINCROSS:
|
case CIRCLE_MINCROSS:
|
||||||
return CircleLayoutAlgorithm.<V> builder()
|
return CircleLayoutAlgorithm.<AttributedVertex> builder()
|
||||||
.reduceEdgeCrossing(true);
|
.reduceEdgeCrossing(true);
|
||||||
case TIDIER_RADIAL_TREE:
|
case TIDIER_RADIAL_TREE:
|
||||||
return TidierRadialTreeLayoutAlgorithm.<V, E> edgeAwareBuilder();
|
return TidierRadialTreeLayoutAlgorithm
|
||||||
|
.<AttributedVertex, AttributedEdge> edgeAwareBuilder();
|
||||||
case MIN_CROSS_TOP_DOWN:
|
case MIN_CROSS_TOP_DOWN:
|
||||||
return EiglspergerLayoutAlgorithm
|
return EiglspergerLayoutAlgorithm
|
||||||
.<V, E> edgeAwareBuilder()
|
.<AttributedVertex, AttributedEdge> edgeAwareBuilder()
|
||||||
.layering(Layering.TOP_DOWN);
|
.layering(Layering.TOP_DOWN);
|
||||||
case MIN_CROSS_LONGEST_PATH:
|
case MIN_CROSS_LONGEST_PATH:
|
||||||
return EiglspergerLayoutAlgorithm
|
return EiglspergerLayoutAlgorithm
|
||||||
.<V, E> edgeAwareBuilder()
|
.<AttributedVertex, AttributedEdge> edgeAwareBuilder()
|
||||||
.layering(Layering.LONGEST_PATH);
|
.layering(Layering.LONGEST_PATH);
|
||||||
case MIN_CROSS_NETWORK_SIMPLEX:
|
case MIN_CROSS_NETWORK_SIMPLEX:
|
||||||
return EiglspergerLayoutAlgorithm
|
return EiglspergerLayoutAlgorithm
|
||||||
.<V, E> edgeAwareBuilder()
|
.<AttributedVertex, AttributedEdge> edgeAwareBuilder()
|
||||||
.layering(Layering.NETWORK_SIMPLEX);
|
.layering(Layering.NETWORK_SIMPLEX);
|
||||||
case MIN_CROSS_COFFMAN_GRAHAM:
|
case MIN_CROSS_COFFMAN_GRAHAM:
|
||||||
return EiglspergerLayoutAlgorithm
|
return EiglspergerLayoutAlgorithm
|
||||||
.<V, E> edgeAwareBuilder()
|
.<AttributedVertex, AttributedEdge> edgeAwareBuilder()
|
||||||
.layering(Layering.COFFMAN_GRAHAM);
|
.layering(Layering.COFFMAN_GRAHAM);
|
||||||
case RADIAL:
|
case RADIAL:
|
||||||
return RadialTreeLayoutAlgorithm
|
return RadialTreeLayoutAlgorithm
|
||||||
.<V> builder()
|
.<AttributedVertex> builder()
|
||||||
.verticalVertexSpacing(300);
|
.verticalVertexSpacing(300);
|
||||||
case BALLOON:
|
case BALLOON:
|
||||||
return BalloonLayoutAlgorithm
|
return BalloonLayoutAlgorithm
|
||||||
.<V> builder()
|
.<AttributedVertex> builder()
|
||||||
.verticalVertexSpacing(300);
|
.verticalVertexSpacing(300);
|
||||||
case TREE:
|
case TREE:
|
||||||
return TreeLayoutAlgorithm
|
return TreeLayoutAlgorithm
|
||||||
.builder();
|
.builder();
|
||||||
case TIDIER_TREE:
|
case TIDIER_TREE:
|
||||||
default:
|
default:
|
||||||
return TidierTreeLayoutAlgorithm.<V, E> edgeAwareBuilder();
|
return TidierTreeLayoutAlgorithm
|
||||||
|
.<AttributedVertex, AttributedEdge> edgeAwareBuilder();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,61 +15,57 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.graph.visualization;
|
package ghidra.graph.visualization;
|
||||||
|
|
||||||
import org.jungrapht.visualization.RenderContext;
|
import static ghidra.graph.visualization.LayoutFunction.*;
|
||||||
import org.jungrapht.visualization.VisualizationServer;
|
|
||||||
import org.jungrapht.visualization.layout.algorithms.Balloon;
|
|
||||||
import org.jungrapht.visualization.layout.algorithms.BalloonLayoutAlgorithm;
|
|
||||||
import org.jungrapht.visualization.layout.algorithms.EdgeSorting;
|
|
||||||
import org.jungrapht.visualization.layout.algorithms.Layered;
|
|
||||||
import org.jungrapht.visualization.layout.algorithms.LayoutAlgorithm;
|
|
||||||
import org.jungrapht.visualization.layout.algorithms.RadialTreeLayout;
|
|
||||||
import org.jungrapht.visualization.layout.algorithms.RadialTreeLayoutAlgorithm;
|
|
||||||
import org.jungrapht.visualization.layout.algorithms.TreeLayout;
|
|
||||||
import org.jungrapht.visualization.layout.algorithms.util.VertexBoundsFunctionConsumer;
|
|
||||||
import org.jungrapht.visualization.layout.model.Rectangle;
|
|
||||||
import org.jungrapht.visualization.util.LayoutAlgorithmTransition;
|
|
||||||
import org.jungrapht.visualization.util.LayoutPaintable;
|
|
||||||
|
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
import static ghidra.graph.visualization.LayoutFunction.TIDIER_TREE;
|
import org.jungrapht.visualization.RenderContext;
|
||||||
|
import org.jungrapht.visualization.VisualizationServer;
|
||||||
|
import org.jungrapht.visualization.layout.algorithms.*;
|
||||||
|
import org.jungrapht.visualization.layout.algorithms.util.VertexBoundsFunctionConsumer;
|
||||||
|
import org.jungrapht.visualization.layout.model.Rectangle;
|
||||||
|
import org.jungrapht.visualization.util.LayoutAlgorithmTransition;
|
||||||
|
import org.jungrapht.visualization.util.LayoutPaintable;
|
||||||
|
|
||||||
|
import ghidra.service.graph.AttributedEdge;
|
||||||
|
import ghidra.service.graph.AttributedVertex;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages the selection and transition from one {@link LayoutAlgorithm} to another
|
* Manages the selection and transition from one {@link LayoutAlgorithm} to another
|
||||||
*/
|
*/
|
||||||
class LayoutTransitionManager<V, E> {
|
class LayoutTransitionManager {
|
||||||
|
|
||||||
LayoutFunction layoutFunction = new LayoutFunction();
|
LayoutFunction layoutFunction = new LayoutFunction();
|
||||||
/**
|
/**
|
||||||
* the {@link VisualizationServer} used to display graphs using the requested {@link LayoutAlgorithm}
|
* the {@link VisualizationServer} used to display graphs using the requested {@link LayoutAlgorithm}
|
||||||
*/
|
*/
|
||||||
VisualizationServer<V, E> visualizationServer;
|
VisualizationServer<AttributedVertex, AttributedEdge> visualizationServer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* a {@link Predicate} to assist in determining which vertices are root vertices (for Tree layouts)
|
* a {@link Predicate} to assist in determining which vertices are root vertices (for Tree layouts)
|
||||||
*/
|
*/
|
||||||
Predicate<V> rootPredicate;
|
Predicate<AttributedVertex> rootPredicate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* a {@link Comparator} to sort edges during layout graph traversal
|
* a {@link Comparator} to sort edges during layout graph traversal
|
||||||
*/
|
*/
|
||||||
Comparator<E> edgeComparator = (e1, e2) -> 0;
|
Comparator<AttributedEdge> edgeComparator = (e1, e2) -> 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* a {@link Function} to provide {@link Rectangle} (and thus bounds} for vertices
|
* a {@link Function} to provide {@link Rectangle} (and thus bounds} for vertices
|
||||||
*/
|
*/
|
||||||
Function<V, Rectangle> vertexBoundsFunction;
|
Function<AttributedVertex, Rectangle> vertexBoundsFunction;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* the {@link RenderContext} used to draw the graph
|
* the {@link RenderContext} used to draw the graph
|
||||||
*/
|
*/
|
||||||
RenderContext<V, E> renderContext;
|
RenderContext<AttributedVertex, AttributedEdge> renderContext;
|
||||||
|
|
||||||
LayoutPaintable.BalloonRings<V, E> balloonLayoutRings;
|
LayoutPaintable.BalloonRings<AttributedVertex, AttributedEdge> balloonLayoutRings;
|
||||||
|
|
||||||
LayoutPaintable.RadialRings<V> radialLayoutRings;
|
LayoutPaintable.RadialRings<AttributedVertex> radialLayoutRings;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -78,8 +74,8 @@ class LayoutTransitionManager<V, E> {
|
||||||
* @param rootPredicate selects root vertices
|
* @param rootPredicate selects root vertices
|
||||||
*/
|
*/
|
||||||
public LayoutTransitionManager(
|
public LayoutTransitionManager(
|
||||||
VisualizationServer<V, E> visualizationServer,
|
VisualizationServer<AttributedVertex, AttributedEdge> visualizationServer,
|
||||||
Predicate<V> rootPredicate) {
|
Predicate<AttributedVertex> rootPredicate) {
|
||||||
this.visualizationServer = visualizationServer;
|
this.visualizationServer = visualizationServer;
|
||||||
this.rootPredicate = rootPredicate;
|
this.rootPredicate = rootPredicate;
|
||||||
|
|
||||||
|
@ -87,7 +83,7 @@ class LayoutTransitionManager<V, E> {
|
||||||
this.vertexBoundsFunction = visualizationServer.getRenderContext().getVertexBoundsFunction();
|
this.vertexBoundsFunction = visualizationServer.getRenderContext().getVertexBoundsFunction();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setEdgeComparator(Comparator<E> edgeComparator) {
|
public void setEdgeComparator(Comparator<AttributedEdge> edgeComparator) {
|
||||||
this.edgeComparator = edgeComparator;
|
this.edgeComparator = edgeComparator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,19 +93,19 @@ class LayoutTransitionManager<V, E> {
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public void setLayout(String layoutName) {
|
public void setLayout(String layoutName) {
|
||||||
LayoutAlgorithm.Builder<V, ?, ?> builder = layoutFunction.apply(layoutName);
|
LayoutAlgorithm.Builder<AttributedVertex, ?, ?> builder = layoutFunction.apply(layoutName);
|
||||||
LayoutAlgorithm<V> layoutAlgorithm = builder.build();
|
LayoutAlgorithm<AttributedVertex> layoutAlgorithm = builder.build();
|
||||||
if (layoutAlgorithm instanceof VertexBoundsFunctionConsumer) {
|
if (layoutAlgorithm instanceof VertexBoundsFunctionConsumer) {
|
||||||
((VertexBoundsFunctionConsumer<V>) layoutAlgorithm)
|
((VertexBoundsFunctionConsumer<AttributedVertex>) layoutAlgorithm)
|
||||||
.setVertexBoundsFunction(vertexBoundsFunction);
|
.setVertexBoundsFunction(vertexBoundsFunction);
|
||||||
}
|
}
|
||||||
if (layoutAlgorithm instanceof Layered) {
|
if (layoutAlgorithm instanceof Layered) {
|
||||||
((Layered<V, E>)layoutAlgorithm)
|
((Layered<AttributedVertex, AttributedEdge>) layoutAlgorithm)
|
||||||
.setMaxLevelCrossFunction(g ->
|
.setMaxLevelCrossFunction(g ->
|
||||||
Math.max(1, Math.min(10, 500 / g.vertexSet().size())));
|
Math.max(1, Math.min(10, 500 / g.vertexSet().size())));
|
||||||
}
|
}
|
||||||
if (layoutAlgorithm instanceof TreeLayout) {
|
if (layoutAlgorithm instanceof TreeLayout) {
|
||||||
((TreeLayout<V>) layoutAlgorithm).setRootPredicate(rootPredicate);
|
((TreeLayout<AttributedVertex>) layoutAlgorithm).setRootPredicate(rootPredicate);
|
||||||
}
|
}
|
||||||
// remove any previously added layout paintables
|
// remove any previously added layout paintables
|
||||||
removePaintable(radialLayoutRings);
|
removePaintable(radialLayoutRings);
|
||||||
|
@ -117,18 +113,19 @@ class LayoutTransitionManager<V, E> {
|
||||||
if (layoutAlgorithm instanceof BalloonLayoutAlgorithm) {
|
if (layoutAlgorithm instanceof BalloonLayoutAlgorithm) {
|
||||||
balloonLayoutRings =
|
balloonLayoutRings =
|
||||||
new LayoutPaintable.BalloonRings<>(
|
new LayoutPaintable.BalloonRings<>(
|
||||||
visualizationServer, (BalloonLayoutAlgorithm<V>) layoutAlgorithm);
|
visualizationServer,
|
||||||
|
(BalloonLayoutAlgorithm<AttributedVertex>) layoutAlgorithm);
|
||||||
visualizationServer.addPreRenderPaintable(balloonLayoutRings);
|
visualizationServer.addPreRenderPaintable(balloonLayoutRings);
|
||||||
}
|
}
|
||||||
if (layoutAlgorithm instanceof RadialTreeLayout) {
|
if (layoutAlgorithm instanceof RadialTreeLayout) {
|
||||||
radialLayoutRings =
|
radialLayoutRings =
|
||||||
new LayoutPaintable.RadialRings<>(
|
new LayoutPaintable.RadialRings<>(
|
||||||
visualizationServer, (RadialTreeLayout<V>) layoutAlgorithm);
|
visualizationServer, (RadialTreeLayout<AttributedVertex>) layoutAlgorithm);
|
||||||
visualizationServer.addPreRenderPaintable(radialLayoutRings);
|
visualizationServer.addPreRenderPaintable(radialLayoutRings);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (layoutAlgorithm instanceof EdgeSorting) {
|
if (layoutAlgorithm instanceof EdgeSorting) {
|
||||||
((EdgeSorting<E>) layoutAlgorithm).setEdgeComparator(edgeComparator);
|
((EdgeSorting<AttributedEdge>) layoutAlgorithm).setEdgeComparator(edgeComparator);
|
||||||
}
|
}
|
||||||
LayoutAlgorithmTransition.apply(visualizationServer, layoutAlgorithm);
|
LayoutAlgorithmTransition.apply(visualizationServer, layoutAlgorithm);
|
||||||
}
|
}
|
||||||
|
@ -140,22 +137,22 @@ class LayoutTransitionManager<V, E> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public LayoutAlgorithm<V> getInitialLayoutAlgorithm() {
|
public LayoutAlgorithm<AttributedVertex> getInitialLayoutAlgorithm() {
|
||||||
LayoutAlgorithm<V> initialLayoutAlgorithm =
|
LayoutAlgorithm<AttributedVertex> initialLayoutAlgorithm =
|
||||||
layoutFunction.apply(TIDIER_TREE).build();
|
layoutFunction.apply(TIDIER_TREE).build();
|
||||||
|
|
||||||
if (initialLayoutAlgorithm instanceof TreeLayout) {
|
if (initialLayoutAlgorithm instanceof TreeLayout) {
|
||||||
((TreeLayout<V>) initialLayoutAlgorithm)
|
((TreeLayout<AttributedVertex>) initialLayoutAlgorithm)
|
||||||
.setRootPredicate(rootPredicate);
|
.setRootPredicate(rootPredicate);
|
||||||
((TreeLayout<V>) initialLayoutAlgorithm)
|
((TreeLayout<AttributedVertex>) initialLayoutAlgorithm)
|
||||||
.setVertexBoundsFunction(vertexBoundsFunction);
|
.setVertexBoundsFunction(vertexBoundsFunction);
|
||||||
}
|
}
|
||||||
if (initialLayoutAlgorithm instanceof EdgeSorting) {
|
if (initialLayoutAlgorithm instanceof EdgeSorting) {
|
||||||
((EdgeSorting<E>) initialLayoutAlgorithm)
|
((EdgeSorting<AttributedEdge>) initialLayoutAlgorithm)
|
||||||
.setEdgeComparator(edgeComparator);
|
.setEdgeComparator(edgeComparator);
|
||||||
}
|
}
|
||||||
if (initialLayoutAlgorithm instanceof VertexBoundsFunctionConsumer) {
|
if (initialLayoutAlgorithm instanceof VertexBoundsFunctionConsumer) {
|
||||||
((VertexBoundsFunctionConsumer<V>) initialLayoutAlgorithm)
|
((VertexBoundsFunctionConsumer<AttributedVertex>) initialLayoutAlgorithm)
|
||||||
.setVertexBoundsFunction(vertexBoundsFunction);
|
.setVertexBoundsFunction(vertexBoundsFunction);
|
||||||
}
|
}
|
||||||
return initialLayoutAlgorithm;
|
return initialLayoutAlgorithm;
|
||||||
|
|
|
@ -18,6 +18,7 @@ package ghidra.graph.program;
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
import docking.widgets.EventTrigger;
|
||||||
import ghidra.app.plugin.core.colorizer.ColorizingService;
|
import ghidra.app.plugin.core.colorizer.ColorizingService;
|
||||||
import ghidra.framework.plugintool.PluginTool;
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
|
@ -156,12 +157,15 @@ public class BlockGraphTask extends Task {
|
||||||
display.setGraph(graph, actionName, appendGraph, monitor);
|
display.setGraph(graph, actionName, appendGraph, monitor);
|
||||||
|
|
||||||
if (location != null) {
|
if (location != null) {
|
||||||
display.setLocation(listener.getVertexIdForAddress(location.getAddress()));
|
// initialize the graph location, but don't have the graph send an event
|
||||||
|
String id = listener.getVertexIdForAddress(location.getAddress());
|
||||||
|
display.setLocationFocus(id, EventTrigger.INTERNAL_ONLY);
|
||||||
}
|
}
|
||||||
if (selection != null && !selection.isEmpty()) {
|
if (selection != null && !selection.isEmpty()) {
|
||||||
List<String> selectedVertices = listener.getVertices(selection);
|
List<String> selectedVertices = listener.getVertices(selection);
|
||||||
if (selectedVertices != null) {
|
if (selectedVertices != null) {
|
||||||
display.selectVertices(selectedVertices);
|
// intialize the graph selection, but don't have the graph send an event
|
||||||
|
display.selectVertices(selectedVertices, EventTrigger.INTERNAL_ONLY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ package ghidra.graph.program;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
import docking.widgets.EventTrigger;
|
||||||
import ghidra.service.graph.*;
|
import ghidra.service.graph.*;
|
||||||
import ghidra.util.exception.CancelledException;
|
import ghidra.util.exception.CancelledException;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
@ -37,7 +38,7 @@ public class TestGraphDisplay implements GraphDisplay {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setLocation(String vertexID) {
|
public void setLocationFocus(String vertexID, EventTrigger eventTrigger) {
|
||||||
currentFocusedVertex = vertexID;
|
currentFocusedVertex = vertexID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,7 +47,7 @@ public class TestGraphDisplay implements GraphDisplay {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void selectVertices(List<String> vertexList) {
|
public void selectVertices(List<String> vertexList, EventTrigger eventTrigger) {
|
||||||
currentSelection = vertexList;
|
currentSelection = vertexList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,7 +104,7 @@ public class TestGraphDisplay implements GraphDisplay {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void focusChanged(String vertexId) {
|
public void focusChanged(String vertexId) {
|
||||||
listener.locationChanged(vertexId);
|
listener.locationFocusChanged(vertexId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void selectionChanged(List<String> vertexIds) {
|
public void selectionChanged(List<String> vertexIds) {
|
||||||
|
|
|
@ -30,7 +30,7 @@ public class DummyGraphDisplayListener implements GraphDisplayListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void locationChanged(String vertexId) {
|
public void locationFocusChanged(String vertexId) {
|
||||||
// I'm a dummy
|
// I'm a dummy
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ package ghidra.service.graph;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import docking.widgets.EventTrigger;
|
||||||
import ghidra.util.exception.CancelledException;
|
import ghidra.util.exception.CancelledException;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
|
@ -41,18 +42,31 @@ public interface GraphDisplay {
|
||||||
public void setGraphDisplayListener(GraphDisplayListener listener);
|
public void setGraphDisplayListener(GraphDisplayListener listener);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tells the graph display window to focus
|
* Tells the graph display window to focus the vertex with the given id.
|
||||||
*
|
*
|
||||||
* @param vertexID the id of the vertex to focus
|
* @param vertexID the id of the vertex to focus
|
||||||
|
* @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
|
||||||
|
* the {@link GraphDisplayListener#locationFocusChanged(String)}. For example, if we are updating
|
||||||
|
* the the location due to an event from the main application, we don't want to notify the
|
||||||
|
* application the graph changed to avoid event cycles. See {@link EventTrigger} for more
|
||||||
|
* information.
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
public void setLocation(String vertexID);
|
public void setLocationFocus(String vertexID, EventTrigger eventTrigger);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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 vertexList the list of vertex ids to select
|
* @param vertexList the list of vertex ids to select
|
||||||
|
* @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
|
||||||
|
* the {@link GraphDisplayListener#locationFocusChanged(String)}. For example, if we are updating
|
||||||
|
* the the location due to an event from the main application, we don't want to notify the
|
||||||
|
* application the graph changed to avoid event cycles. See {@link EventTrigger} for more
|
||||||
|
* information.
|
||||||
*/
|
*/
|
||||||
public void selectVertices(List<String> vertexList);
|
public void selectVertices(List<String> vertexList, EventTrigger eventTrigger);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Closes this graph display window.
|
* Closes this graph display window.
|
||||||
|
@ -103,7 +117,7 @@ public interface GraphDisplay {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates a vertex to a new name
|
* Updates a vertex to a new name
|
||||||
* @param id the vertix id
|
* @param id the vertex id
|
||||||
* @param newName the new name of the vertex
|
* @param newName the new name of the vertex
|
||||||
*/
|
*/
|
||||||
public void updateVertexName(String id, String newName);
|
public void updateVertexName(String id, String newName);
|
||||||
|
|
|
@ -37,7 +37,7 @@ public interface GraphDisplayListener {
|
||||||
* Notification that the "focused" (active) vertex has changed.
|
* Notification that the "focused" (active) vertex has changed.
|
||||||
* @param vertexId the vertex id of the currently "focused" vertex
|
* @param vertexId the vertex id of the currently "focused" vertex
|
||||||
*/
|
*/
|
||||||
public void locationChanged(String vertexId);
|
public void locationFocusChanged(String vertexId);
|
||||||
|
|
||||||
default boolean updateVertexName(String vertexId, String oldName, String newName) {
|
default boolean updateVertexName(String vertexId, String oldName, String newName) {
|
||||||
// no op
|
// no op
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue