mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-06 03:50:02 +02:00
Bug fix and test fixes for recent graph changes
This commit is contained in:
parent
ada1bbec85
commit
58847ff821
3 changed files with 407 additions and 3 deletions
|
@ -0,0 +1,402 @@
|
|||
/* ###
|
||||
* IP: BSD
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2005, The JUNG Authors
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* This software is open-source under the BSD license; see either
|
||||
* "license.txt" or
|
||||
* https://github.com/jrtom/jung/blob/master/LICENSE for a description.
|
||||
* Created on Mar 8, 2005
|
||||
*
|
||||
*/
|
||||
package ghidra.graph.viewer.event.mouse;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.util.Collection;
|
||||
|
||||
import javax.swing.JComponent;
|
||||
|
||||
import edu.uci.ics.jung.algorithms.layout.GraphElementAccessor;
|
||||
import edu.uci.ics.jung.algorithms.layout.Layout;
|
||||
import edu.uci.ics.jung.visualization.Layer;
|
||||
import edu.uci.ics.jung.visualization.VisualizationServer.Paintable;
|
||||
import edu.uci.ics.jung.visualization.VisualizationViewer;
|
||||
import edu.uci.ics.jung.visualization.control.AbstractGraphMousePlugin;
|
||||
import edu.uci.ics.jung.visualization.picking.PickedState;
|
||||
|
||||
/**
|
||||
* PickingGraphMousePlugin supports the picking of graph elements
|
||||
* with the mouse. MouseButtonOne picks a single vertex
|
||||
* or edge, and MouseButtonTwo adds to the set of selected Vertices
|
||||
* or EdgeType. If a Vertex is selected and the mouse is dragged while
|
||||
* on the selected Vertex, then that Vertex will be repositioned to
|
||||
* follow the mouse until the button is released.
|
||||
*
|
||||
* @author Tom Nelson
|
||||
*
|
||||
*
|
||||
* Note: this class was copied completely from Jung 2. Minimal changes were applied to get
|
||||
* correct mouse behavior by using {@link MouseEvent#getModifiersEx()}.
|
||||
* @param <V> the vertex type
|
||||
* @param <E> the edge type
|
||||
*
|
||||
*/
|
||||
public class JungPickingGraphMousePlugin<V, E> extends AbstractGraphMousePlugin
|
||||
implements MouseListener, MouseMotionListener {
|
||||
|
||||
/**
|
||||
* the picked Vertex, if any
|
||||
*/
|
||||
protected V vertex;
|
||||
|
||||
/**
|
||||
* the picked Edge, if any
|
||||
*/
|
||||
protected E edge;
|
||||
|
||||
/**
|
||||
* the x distance from the picked vertex center to the mouse point
|
||||
*/
|
||||
protected double offsetx;
|
||||
|
||||
/**
|
||||
* the y distance from the picked vertex center to the mouse point
|
||||
*/
|
||||
protected double offsety;
|
||||
|
||||
/**
|
||||
* controls whether the Vertices may be moved with the mouse
|
||||
*/
|
||||
protected boolean locked;
|
||||
|
||||
/**
|
||||
* additional modifiers for the action of adding to an existing
|
||||
* selection
|
||||
*/
|
||||
protected int addToSelectionModifiers;
|
||||
|
||||
/**
|
||||
* used to draw a rectangle to contain picked vertices
|
||||
*/
|
||||
protected Rectangle2D rect = new Rectangle2D.Float();
|
||||
|
||||
/**
|
||||
* the Paintable for the lens picking rectangle
|
||||
*/
|
||||
protected Paintable lensPaintable;
|
||||
|
||||
/**
|
||||
* color for the picking rectangle
|
||||
*/
|
||||
protected Color lensColor = Color.cyan;
|
||||
|
||||
/**
|
||||
* create an instance with default settings
|
||||
*/
|
||||
public JungPickingGraphMousePlugin() {
|
||||
this(InputEvent.BUTTON1_DOWN_MASK,
|
||||
InputEvent.BUTTON1_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK);
|
||||
}
|
||||
|
||||
/**
|
||||
* create an instance with overrides
|
||||
* @param selectionModifiers for primary selection
|
||||
* @param addToSelectionModifiers for additional selection
|
||||
*/
|
||||
public JungPickingGraphMousePlugin(int selectionModifiers, int addToSelectionModifiers) {
|
||||
super(selectionModifiers);
|
||||
this.addToSelectionModifiers = addToSelectionModifiers;
|
||||
this.lensPaintable = new LensPaintable();
|
||||
this.cursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the lensColor.
|
||||
*/
|
||||
public Color getLensColor() {
|
||||
return lensColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param lensColor The lensColor to set.
|
||||
*/
|
||||
public void setLensColor(Color lensColor) {
|
||||
this.lensColor = lensColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* a Paintable to draw the rectangle used to pick multiple
|
||||
* Vertices
|
||||
* @author Tom Nelson
|
||||
*
|
||||
*/
|
||||
class LensPaintable implements Paintable {
|
||||
|
||||
@Override
|
||||
public void paint(Graphics g) {
|
||||
Color oldColor = g.getColor();
|
||||
g.setColor(lensColor);
|
||||
((Graphics2D) g).draw(rect);
|
||||
g.setColor(oldColor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useTransform() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For primary modifiers (default, MouseButton1):
|
||||
* pick a single Vertex or Edge that
|
||||
* is under the mouse pointer. If no Vertex or edge is under
|
||||
* the pointer, unselect all picked Vertices and edges, and
|
||||
* set up to draw a rectangle for multiple selection
|
||||
* of contained Vertices.
|
||||
* For additional selection (default Shift+MouseButton1):
|
||||
* Add to the selection, a single Vertex or Edge that is
|
||||
* under the mouse pointer. If a previously picked Vertex
|
||||
* or Edge is under the pointer, it is un-picked.
|
||||
* If no vertex or Edge is under the pointer, set up
|
||||
* to draw a multiple selection rectangle (as above)
|
||||
* but do not unpick previously picked elements.
|
||||
*
|
||||
* @param e the event
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void mousePressed(MouseEvent e) {
|
||||
down = e.getPoint();
|
||||
VisualizationViewer<V, E> vv = (VisualizationViewer<V, E>) e.getSource();
|
||||
GraphElementAccessor<V, E> pickSupport = vv.getPickSupport();
|
||||
PickedState<V> pickedVertexState = vv.getPickedVertexState();
|
||||
PickedState<E> pickedEdgeState = vv.getPickedEdgeState();
|
||||
if (pickSupport != null && pickedVertexState != null) {
|
||||
Layout<V, E> layout = vv.getGraphLayout();
|
||||
if (e.getModifiersEx() == modifiers) {
|
||||
rect.setFrameFromDiagonal(down, down);
|
||||
// p is the screen point for the mouse event
|
||||
Point2D ip = e.getPoint();
|
||||
|
||||
vertex = pickSupport.getVertex(layout, ip.getX(), ip.getY());
|
||||
if (vertex != null) {
|
||||
if (pickedVertexState.isPicked(vertex) == false) {
|
||||
pickedVertexState.clear();
|
||||
pickedVertexState.pick(vertex, true);
|
||||
}
|
||||
// layout.getLocation applies the layout Function so
|
||||
// q is transformed by the layout Function only
|
||||
Point2D q = layout.apply(vertex);
|
||||
// transform the mouse point to graph coordinate system
|
||||
Point2D gp = vv.getRenderContext()
|
||||
.getMultiLayerTransformer()
|
||||
.inverseTransform(Layer.LAYOUT, ip);
|
||||
|
||||
offsetx = (float) (gp.getX() - q.getX());
|
||||
offsety = (float) (gp.getY() - q.getY());
|
||||
}
|
||||
else if ((edge = pickSupport.getEdge(layout, ip.getX(), ip.getY())) != null) {
|
||||
pickedEdgeState.clear();
|
||||
pickedEdgeState.pick(edge, true);
|
||||
}
|
||||
else {
|
||||
vv.addPostRenderPaintable(lensPaintable);
|
||||
pickedEdgeState.clear();
|
||||
pickedVertexState.clear();
|
||||
}
|
||||
|
||||
}
|
||||
else if (e.getModifiersEx() == addToSelectionModifiers) {
|
||||
vv.addPostRenderPaintable(lensPaintable);
|
||||
rect.setFrameFromDiagonal(down, down);
|
||||
Point2D ip = e.getPoint();
|
||||
vertex = pickSupport.getVertex(layout, ip.getX(), ip.getY());
|
||||
if (vertex != null) {
|
||||
boolean wasThere =
|
||||
pickedVertexState.pick(vertex, !pickedVertexState.isPicked(vertex));
|
||||
if (wasThere) {
|
||||
vertex = null;
|
||||
}
|
||||
else {
|
||||
|
||||
// layout.getLocation applies the layout Function so
|
||||
// q is transformed by the layout Function only
|
||||
Point2D q = layout.apply(vertex);
|
||||
// translate mouse point to graph coord system
|
||||
Point2D gp = vv.getRenderContext()
|
||||
.getMultiLayerTransformer()
|
||||
.inverseTransform(Layer.LAYOUT, ip);
|
||||
|
||||
offsetx = (float) (gp.getX() - q.getX());
|
||||
offsety = (float) (gp.getY() - q.getY());
|
||||
}
|
||||
}
|
||||
else if ((edge = pickSupport.getEdge(layout, ip.getX(), ip.getY())) != null) {
|
||||
pickedEdgeState.pick(edge, !pickedEdgeState.isPicked(edge));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (vertex != null) {
|
||||
e.consume();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the mouse is dragging a rectangle, pick the
|
||||
* Vertices contained in that rectangle
|
||||
*
|
||||
* clean up settings from mousePressed
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void mouseReleased(MouseEvent e) {
|
||||
VisualizationViewer<V, E> vv = (VisualizationViewer<V, E>) e.getSource();
|
||||
if (e.getModifiersEx() == modifiers) {
|
||||
if (down != null) {
|
||||
Point2D out = e.getPoint();
|
||||
|
||||
if (vertex == null && heyThatsTooClose(down, out, 5) == false) {
|
||||
pickContainedVertices(vv, down, out, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (e.getModifiersEx() == this.addToSelectionModifiers) {
|
||||
if (down != null) {
|
||||
Point2D out = e.getPoint();
|
||||
|
||||
if (vertex == null && heyThatsTooClose(down, out, 5) == false) {
|
||||
pickContainedVertices(vv, down, out, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
down = null;
|
||||
vertex = null;
|
||||
edge = null;
|
||||
rect.setFrame(0, 0, 0, 0);
|
||||
vv.removePostRenderPaintable(lensPaintable);
|
||||
vv.repaint();
|
||||
}
|
||||
|
||||
/**
|
||||
* If the mouse is over a picked vertex, drag all picked
|
||||
* vertices with the mouse.
|
||||
* If the mouse is not over a Vertex, draw the rectangle
|
||||
* to select multiple Vertices
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void mouseDragged(MouseEvent e) {
|
||||
if (locked == false) {
|
||||
VisualizationViewer<V, E> vv = (VisualizationViewer<V, E>) e.getSource();
|
||||
if (vertex != null) {
|
||||
Point p = e.getPoint();
|
||||
Point2D graphPoint =
|
||||
vv.getRenderContext().getMultiLayerTransformer().inverseTransform(p);
|
||||
Point2D graphDown =
|
||||
vv.getRenderContext().getMultiLayerTransformer().inverseTransform(down);
|
||||
Layout<V, E> layout = vv.getGraphLayout();
|
||||
double dx = graphPoint.getX() - graphDown.getX();
|
||||
double dy = graphPoint.getY() - graphDown.getY();
|
||||
PickedState<V> ps = vv.getPickedVertexState();
|
||||
|
||||
for (V v : ps.getPicked()) {
|
||||
Point2D vp = layout.apply(v);
|
||||
vp.setLocation(vp.getX() + dx, vp.getY() + dy);
|
||||
layout.setLocation(v, vp);
|
||||
}
|
||||
down = p;
|
||||
|
||||
}
|
||||
else {
|
||||
Point2D out = e.getPoint();
|
||||
if (e.getModifiersEx() == this.addToSelectionModifiers ||
|
||||
e.getModifiersEx() == modifiers) {
|
||||
rect.setFrameFromDiagonal(down, out);
|
||||
}
|
||||
}
|
||||
if (vertex != null) {
|
||||
e.consume();
|
||||
}
|
||||
vv.repaint();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean heyThatsTooClose(Point2D p, Point2D q, double min) {
|
||||
return Math.abs(p.getX() - q.getX()) < min &&
|
||||
Math.abs(p.getY() - q.getY()) < min;
|
||||
}
|
||||
|
||||
/**
|
||||
* pick the vertices inside the rectangle created from points 'down' and 'out' (two diagonally
|
||||
* opposed corners of the rectangle)
|
||||
*
|
||||
* @param vv the viewer containing the layout and picked state
|
||||
* @param down one corner of the rectangle
|
||||
* @param out the other corner of the rectangle
|
||||
* @param clear whether to reset existing picked state
|
||||
*/
|
||||
protected void pickContainedVertices(VisualizationViewer<V, E> vv, Point2D down, Point2D out,
|
||||
boolean clear) {
|
||||
|
||||
Layout<V, E> layout = vv.getGraphLayout();
|
||||
PickedState<V> pickedVertexState = vv.getPickedVertexState();
|
||||
|
||||
Rectangle2D pickRectangle = new Rectangle2D.Double();
|
||||
pickRectangle.setFrameFromDiagonal(down, out);
|
||||
|
||||
if (pickedVertexState != null) {
|
||||
if (clear) {
|
||||
pickedVertexState.clear();
|
||||
}
|
||||
GraphElementAccessor<V, E> pickSupport = vv.getPickSupport();
|
||||
|
||||
Collection<V> picked = pickSupport.getVertices(layout, pickRectangle);
|
||||
for (V v : picked) {
|
||||
pickedVertexState.pick(v, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
// stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseEntered(MouseEvent e) {
|
||||
JComponent c = (JComponent) e.getSource();
|
||||
c.setCursor(cursor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseExited(MouseEvent e) {
|
||||
JComponent c = (JComponent) e.getSource();
|
||||
c.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseMoved(MouseEvent e) {
|
||||
// stub
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the locked.
|
||||
*/
|
||||
public boolean isLocked() {
|
||||
return locked;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param locked The locked to set.
|
||||
*/
|
||||
public void setLocked(boolean locked) {
|
||||
this.locked = locked;
|
||||
}
|
||||
}
|
|
@ -28,12 +28,11 @@ import edu.uci.ics.jung.algorithms.layout.GraphElementAccessor;
|
|||
import edu.uci.ics.jung.algorithms.layout.Layout;
|
||||
import edu.uci.ics.jung.graph.Graph;
|
||||
import edu.uci.ics.jung.visualization.*;
|
||||
import edu.uci.ics.jung.visualization.control.PickingGraphMousePlugin;
|
||||
import edu.uci.ics.jung.visualization.picking.PickedState;
|
||||
import ghidra.graph.viewer.*;
|
||||
|
||||
public class VisualGraphPickingGraphMousePlugin<V extends VisualVertex, E extends VisualEdge<V>>
|
||||
extends PickingGraphMousePlugin<V, E> implements VisualGraphMousePlugin<V, E> {
|
||||
extends JungPickingGraphMousePlugin<V, E> implements VisualGraphMousePlugin<V, E> {
|
||||
|
||||
// ALERT: -this class was created because mouseDragged() has a bug that generates a NPE
|
||||
// -also, mousePressed() has a bug in that it does not check the modifiers when the method is entered
|
||||
|
|
|
@ -17,6 +17,9 @@ package ghidra.service.graph;
|
|||
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.apache.commons.text.StringEscapeUtils;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
|
@ -99,7 +102,7 @@ public class AttributedVertex extends Attributed {
|
|||
for (Map.Entry<String, String> entry : entries) {
|
||||
buf.append(entry.getKey());
|
||||
buf.append(":");
|
||||
buf.append(entry.getValue());
|
||||
buf.append(StringEscapeUtils.escapeHtml4(entry.getValue()));
|
||||
buf.append("<br>");
|
||||
}
|
||||
htmlString = buf.toString();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue