mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 18:29:37 +02:00
GT-3020 - Function Graph - for edges, split 'in active path' state into
2 parts so we can track hovered/focused/selected separately, which prevents the focused/selected state changes from overwriting each other
This commit is contained in:
parent
85e25af7e8
commit
cb94773ce5
12 changed files with 368 additions and 218 deletions
|
@ -34,7 +34,8 @@ public class FGEdgeImpl implements FGEdge {
|
||||||
boolean doHashCode = true;
|
boolean doHashCode = true;
|
||||||
int hashCode;
|
int hashCode;
|
||||||
|
|
||||||
private boolean inActivePath = false;
|
private boolean inHoveredPath = false;
|
||||||
|
private boolean inFocusedPath = false;
|
||||||
private boolean selected = false;
|
private boolean selected = false;
|
||||||
private double emphasis = 0D;
|
private double emphasis = 0D;
|
||||||
private double alpha = 1D;
|
private double alpha = 1D;
|
||||||
|
@ -50,13 +51,23 @@ public class FGEdgeImpl implements FGEdge {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isInActivePath() {
|
public boolean isInHoveredVertexPath() {
|
||||||
return inActivePath;
|
return inHoveredPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setInActivePath(boolean inActivePath) {
|
public boolean isInFocusedVertexPath() {
|
||||||
this.inActivePath = inActivePath;
|
return inFocusedPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setInHoveredVertexPath(boolean inPath) {
|
||||||
|
this.inHoveredPath = inPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setInFocusedVertexPath(boolean inPath) {
|
||||||
|
this.inFocusedPath = inPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -135,7 +146,8 @@ public class FGEdgeImpl implements FGEdge {
|
||||||
newEdge.layoutArticulationPoints = newPoints;
|
newEdge.layoutArticulationPoints = newPoints;
|
||||||
|
|
||||||
newEdge.alpha = alpha;
|
newEdge.alpha = alpha;
|
||||||
newEdge.inActivePath = inActivePath;
|
newEdge.inHoveredPath = inHoveredPath;
|
||||||
|
newEdge.inFocusedPath = inFocusedPath;
|
||||||
newEdge.selected = selected;
|
newEdge.selected = selected;
|
||||||
return newEdge;
|
return newEdge;
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,7 +61,7 @@ public class ColorUtils {
|
||||||
// This can be addressed with some polar plotting:
|
// This can be addressed with some polar plotting:
|
||||||
// Let the hue be the degree, and the saturation be the radius, so that the range
|
// Let the hue be the degree, and the saturation be the radius, so that the range
|
||||||
// of values covers an area of a circle of radius 1. Let the circle be centered
|
// of values covers an area of a circle of radius 1. Let the circle be centered
|
||||||
// at the origin. Plot the two colors and compute their distance in euclidean
|
// at the origin. Plot the two colors and compute their distance in Euclidean
|
||||||
// space.
|
// space.
|
||||||
|
|
||||||
// Start by plotting the given background
|
// Start by plotting the given background
|
||||||
|
@ -76,7 +76,7 @@ public class ColorUtils {
|
||||||
|
|
||||||
// It's not pleasant to put two highly-saturated colors next to each other
|
// It's not pleasant to put two highly-saturated colors next to each other
|
||||||
// Because of this restriction, we know that the maximum distance the two plotted
|
// Because of this restriction, we know that the maximum distance the two plotted
|
||||||
// points can be from eachother is 1, because their total distance to the center
|
// points can be from each other is 1, because their total distance to the center
|
||||||
// is at most 1.
|
// is at most 1.
|
||||||
vals[1] = 1.0f - vals[1];
|
vals[1] = 1.0f - vals[1];
|
||||||
|
|
||||||
|
|
|
@ -1052,7 +1052,7 @@ public class GraphViewerUtils {
|
||||||
LinkedList<E> filteredEdges = new LinkedList<>();
|
LinkedList<E> filteredEdges = new LinkedList<>();
|
||||||
if (useHover) {
|
if (useHover) {
|
||||||
for (E edge : edges) {
|
for (E edge : edges) {
|
||||||
if (edge.isInActivePath()) {
|
if (edge.isInHoveredVertexPath()) {
|
||||||
filteredEdges.add(edge);
|
filteredEdges.add(edge);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,9 @@ import ghidra.graph.GEdge;
|
||||||
*
|
*
|
||||||
* <P>An edge can be selected, which means that it has been clicked by the user. Also, an
|
* <P>An edge can be selected, which means that it has been clicked by the user. Also, an
|
||||||
* edge can be part of an active path. This allows the UI to paint the edge differently if it
|
* edge can be part of an active path. This allows the UI to paint the edge differently if it
|
||||||
* is in the active path.
|
* is in the active path. The active path concept applies to both hovered and focused vertices
|
||||||
|
* separately. A hovered vertex is one that the user moves the mouse over; a focused vertex is
|
||||||
|
* one that is selected.
|
||||||
*
|
*
|
||||||
* <A NAME="articulations"></A>
|
* <A NAME="articulations"></A>
|
||||||
* <P><U>Articulations</U> - The start and end points are always part of the
|
* <P><U>Articulations</U> - The start and end points are always part of the
|
||||||
|
@ -44,7 +46,7 @@ import ghidra.graph.GEdge;
|
||||||
public interface VisualEdge<V extends VisualVertex> extends GEdge<V> {
|
public interface VisualEdge<V extends VisualVertex> extends GEdge<V> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets this edge selected
|
* Sets this edge selected. This is usually in response to the user selecting the edge.
|
||||||
*
|
*
|
||||||
* @param selected true to select this edge; false to de-select this vertex
|
* @param selected true to select this edge; false to de-select this vertex
|
||||||
*/
|
*/
|
||||||
|
@ -58,20 +60,36 @@ public interface VisualEdge<V extends VisualVertex> extends GEdge<V> {
|
||||||
public boolean isSelected();
|
public boolean isSelected();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets this edge to be marked as in the active path
|
* Sets this edge to be marked as in the active path of a currently hovered vertex
|
||||||
*
|
*
|
||||||
* @param inActivePath true to be marked as in the active path; false to be marked as not
|
* @param inPath true to be marked as in the active path; false to be marked as not
|
||||||
* in the active path
|
* in the active path
|
||||||
*/
|
*/
|
||||||
public void setInActivePath(boolean inActivePath);
|
public void setInHoveredVertexPath(boolean inPath);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if this edge is part of an active path (this allows the edge to be
|
* Returns true if this edge is part of an active path for a currently hovered
|
||||||
* differently rendered)
|
* vertex (this allows the edge to be differently rendered)
|
||||||
*
|
*
|
||||||
* @return true if this edge is part of the active path
|
* @return true if this edge is part of the active path
|
||||||
*/
|
*/
|
||||||
public boolean isInActivePath();
|
public boolean isInHoveredVertexPath();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets this edge to be marked as in the active path of a currently focused/selected vertex
|
||||||
|
*
|
||||||
|
* @param inPath true to be marked as in the active path; false to be marked as not
|
||||||
|
* in the active path
|
||||||
|
*/
|
||||||
|
public void setInFocusedVertexPath(boolean inPath);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if this edge is part of an active path for a currently focused/selected
|
||||||
|
* vertex (this allows the edge to be differently rendered)
|
||||||
|
*
|
||||||
|
* @return true if this edge is part of the active path
|
||||||
|
*/
|
||||||
|
public boolean isInFocusedVertexPath();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the points (in {@link GraphViewerUtils} View Space) of the articulation
|
* Returns the points (in {@link GraphViewerUtils} View Space) of the articulation
|
||||||
|
|
|
@ -32,9 +32,10 @@ public abstract class AbstractVisualEdge<V extends VisualVertex> implements Visu
|
||||||
private V start;
|
private V start;
|
||||||
private V end;
|
private V end;
|
||||||
|
|
||||||
private boolean selected;
|
private boolean inHoveredPath = false;
|
||||||
private boolean inActivePath;
|
private boolean inFocusedPath = false;
|
||||||
private double alpha = 1.0;
|
private double alpha = 1.0;
|
||||||
|
private boolean selected;
|
||||||
private double emphasis;
|
private double emphasis;
|
||||||
|
|
||||||
private List<Point2D> articulations = new ArrayList<>();
|
private List<Point2D> articulations = new ArrayList<>();
|
||||||
|
@ -65,13 +66,23 @@ public abstract class AbstractVisualEdge<V extends VisualVertex> implements Visu
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setInActivePath(boolean inActivePath) {
|
public boolean isInHoveredVertexPath() {
|
||||||
this.inActivePath = inActivePath;
|
return inHoveredPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isInActivePath() {
|
public boolean isInFocusedVertexPath() {
|
||||||
return inActivePath;
|
return inFocusedPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setInHoveredVertexPath(boolean inPath) {
|
||||||
|
this.inHoveredPath = inPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setInFocusedVertexPath(boolean inPath) {
|
||||||
|
this.inFocusedPath = inPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -81,14 +81,15 @@ import ghidra.graph.viewer.vertex.VisualGraphVertexShapeTransformer;
|
||||||
public abstract class VisualEdgeRenderer<V extends VisualVertex, E extends VisualEdge<V>>
|
public abstract class VisualEdgeRenderer<V extends VisualVertex, E extends VisualEdge<V>>
|
||||||
extends BasicEdgeRenderer<V, E> {
|
extends BasicEdgeRenderer<V, E> {
|
||||||
|
|
||||||
private static final float HOVERED_STROKE_WIDTH = 8.0f;
|
private static final float HOVERED_PATH_STROKE_WIDTH = 8.0f;
|
||||||
private static final float SELECTED_STROKE_WIDTH = 4.0f;
|
private static final float FOCUSED_PATH_STROKE_WIDTH = 4.0f;
|
||||||
|
private static final float SELECTED_STROKE_WIDTH = FOCUSED_PATH_STROKE_WIDTH + 2;
|
||||||
private static final float EMPHASIZED_STOKE_WIDTH = SELECTED_STROKE_WIDTH + 3.0f;
|
private static final float EMPHASIZED_STOKE_WIDTH = SELECTED_STROKE_WIDTH + 3.0f;
|
||||||
|
|
||||||
private float dashingPatternOffset;
|
private float dashingPatternOffset;
|
||||||
|
|
||||||
private Color baseColor = Color.BLACK;
|
private Color defaultBaseColor = Color.BLACK;
|
||||||
private Color highlightColor = Color.GRAY;
|
private Color defaultHighlightColor = Color.GRAY;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the offset value for painting dashed lines. This allows clients to animate the
|
* Sets the offset value for painting dashed lines. This allows clients to animate the
|
||||||
|
@ -103,24 +104,29 @@ public abstract class VisualEdgeRenderer<V extends VisualVertex, E extends Visua
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setBaseColor(Color color) {
|
public void setBaseColor(Color color) {
|
||||||
this.baseColor = color;
|
this.defaultBaseColor = color;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Color getBaseColor(Graph<V, E> g, E e) {
|
public Color getBaseColor(Graph<V, E> g, E e) {
|
||||||
return baseColor;
|
return defaultBaseColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setHighlightColor(Color highlightColor) {
|
public void setHighlightColor(Color highlightColor) {
|
||||||
this.highlightColor = highlightColor;
|
this.defaultHighlightColor = highlightColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Color getHighlightColor(Graph<V, E> g, E e) {
|
public Color getHighlightColor(Graph<V, E> g, E e) {
|
||||||
return highlightColor;
|
return defaultHighlightColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
// template method
|
// template method
|
||||||
protected boolean isInActivePath(E e) {
|
protected boolean isInHoveredVertexPath(E e) {
|
||||||
return e.isInActivePath();
|
return e.isInHoveredVertexPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
// template method
|
||||||
|
protected boolean isInFocusedVertexPath(E e) {
|
||||||
|
return e.isInFocusedVertexPath();
|
||||||
}
|
}
|
||||||
|
|
||||||
// template method
|
// template method
|
||||||
|
@ -153,12 +159,17 @@ public abstract class VisualEdgeRenderer<V extends VisualVertex, E extends Visua
|
||||||
float scalex = (float) g.getTransform().getScaleX();
|
float scalex = (float) g.getTransform().getScaleX();
|
||||||
float scaley = (float) g.getTransform().getScaleY();
|
float scaley = (float) g.getTransform().getScaleY();
|
||||||
|
|
||||||
boolean isActive = isInActivePath(e);
|
boolean isInHoveredPath = isInHoveredVertexPath(e);
|
||||||
|
boolean isInFocusedPath = isInFocusedVertexPath(e);
|
||||||
boolean isSelected = isSelected(e);
|
boolean isSelected = isSelected(e);
|
||||||
boolean isEmphasized = isEmphasiszed(e);
|
boolean isEmphasized = isEmphasiszed(e);
|
||||||
|
|
||||||
Color hoveredColor = getHighlightColor(graph, e);
|
Color highlightColor = getHighlightColor(graph, e);
|
||||||
Color selectedColor = getHighlightColor(graph, e).darker();
|
Color baseColor = getBaseColor(graph, e);
|
||||||
|
Color hoveredColor = highlightColor;
|
||||||
|
Color focusedColor = baseColor;
|
||||||
|
Color selectedColor = highlightColor.darker();
|
||||||
|
Color selectedAccentColor = highlightColor;
|
||||||
|
|
||||||
float scale = StrictMath.min(scalex, scaley);
|
float scale = StrictMath.min(scalex, scaley);
|
||||||
|
|
||||||
|
@ -190,146 +201,195 @@ public abstract class VisualEdgeRenderer<V extends VisualVertex, E extends Visua
|
||||||
|
|
||||||
Context<Graph<V, E>, E> context = Context.<Graph<V, E>, E> getInstance(graph, e);
|
Context<Graph<V, E>, E> context = Context.<Graph<V, E>, E> getInstance(graph, e);
|
||||||
boolean edgeHit = vt.transform(edgeShape).intersects(deviceRectangle);
|
boolean edgeHit = vt.transform(edgeShape).intersects(deviceRectangle);
|
||||||
if (edgeHit) {
|
if (!edgeHit) {
|
||||||
|
return;
|
||||||
Paint oldPaint = g.getPaint();
|
|
||||||
|
|
||||||
// get Paints for filling and drawing
|
|
||||||
// (filling is done first so that drawing and label use same Paint)
|
|
||||||
Paint fillPaint = rc.getEdgeFillPaintTransformer().apply(e);
|
|
||||||
BasicStroke selectedStroke = getSelectedStroke(e, scale);
|
|
||||||
BasicStroke hoverStroke = getHoveredStroke(e, scale);
|
|
||||||
BasicStroke empahsisStroke = getEmphasisStroke(e, scale);
|
|
||||||
|
|
||||||
if (fillPaint != null) {
|
|
||||||
if (isActive) {
|
|
||||||
Stroke saveStroke = g.getStroke();
|
|
||||||
g.setPaint(hoveredColor);
|
|
||||||
g.setStroke(hoverStroke);
|
|
||||||
g.fill(edgeShape);
|
|
||||||
g.setStroke(saveStroke);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isSelected) {
|
|
||||||
Stroke saveStroke = g.getStroke();
|
|
||||||
g.setPaint(selectedColor);
|
|
||||||
g.setStroke(selectedStroke);
|
|
||||||
g.fill(edgeShape);
|
|
||||||
g.setStroke(saveStroke);
|
|
||||||
}
|
|
||||||
|
|
||||||
g.setPaint(fillPaint);
|
|
||||||
g.fill(edgeShape);
|
|
||||||
}
|
|
||||||
|
|
||||||
Paint drawPaint = rc.getEdgeDrawPaintTransformer().apply(e);
|
|
||||||
if (drawPaint != null) {
|
|
||||||
if (isEmphasized) {
|
|
||||||
Stroke saveStroke = g.getStroke();
|
|
||||||
g.setPaint(drawPaint);
|
|
||||||
g.setStroke(empahsisStroke);
|
|
||||||
g.draw(edgeShape);
|
|
||||||
g.setStroke(saveStroke);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isActive) {
|
|
||||||
Stroke saveStroke = g.getStroke();
|
|
||||||
g.setPaint(hoveredColor);
|
|
||||||
g.setStroke(hoverStroke);
|
|
||||||
g.draw(edgeShape);
|
|
||||||
g.setStroke(saveStroke);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isSelected) {
|
|
||||||
Stroke saveStroke = g.getStroke();
|
|
||||||
g.setPaint(selectedColor);
|
|
||||||
g.setStroke(selectedStroke);
|
|
||||||
g.draw(edgeShape);
|
|
||||||
g.setStroke(saveStroke);
|
|
||||||
}
|
|
||||||
|
|
||||||
g.setPaint(drawPaint);
|
|
||||||
g.draw(edgeShape);
|
|
||||||
|
|
||||||
// debug - draw a box around the edge
|
|
||||||
//Rectangle shapeBounds = edgeShape.getBounds();
|
|
||||||
//g.setPaint(Color.ORANGE);
|
|
||||||
//g.draw(shapeBounds);
|
|
||||||
}
|
|
||||||
|
|
||||||
Predicate<Context<Graph<V, E>, E>> predicate = rc.getEdgeArrowPredicate();
|
|
||||||
boolean drawArrow = predicate.apply(context);
|
|
||||||
if (drawArrow) {
|
|
||||||
|
|
||||||
Stroke new_stroke = rc.getEdgeArrowStrokeTransformer().apply(e);
|
|
||||||
Stroke old_stroke = g.getStroke();
|
|
||||||
if (new_stroke != null) {
|
|
||||||
g.setStroke(new_stroke);
|
|
||||||
}
|
|
||||||
|
|
||||||
Shape vs2 = getVertexShapeForArrow(rc, layout, v2); // end vertex
|
|
||||||
|
|
||||||
boolean arrowHit = vt.transform(vs2).intersects(deviceRectangle);
|
|
||||||
Paint arrowFillPaint = rc.getArrowFillPaintTransformer().apply(e);
|
|
||||||
Paint arrowDrawPaint = rc.getArrowDrawPaintTransformer().apply(e);
|
|
||||||
|
|
||||||
if (arrowHit) {
|
|
||||||
|
|
||||||
EdgeArrowRenderingSupport<V, E> arrowRenderingSupport =
|
|
||||||
new BasicEdgeArrowRenderingSupport<>();
|
|
||||||
|
|
||||||
AffineTransform at =
|
|
||||||
arrowRenderingSupport.getArrowTransform(rc, edgeShape, vs2);
|
|
||||||
if (at == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Shape arrow = rc.getEdgeArrowTransformer().apply(context);
|
|
||||||
arrow = scaleArrowForBetterVisibility(rc, arrow);
|
|
||||||
arrow = at.createTransformedShape(arrow);
|
|
||||||
|
|
||||||
if (isEmphasized) {
|
|
||||||
Stroke saveStroke = g.getStroke();
|
|
||||||
g.setPaint(arrowDrawPaint);
|
|
||||||
g.setStroke(empahsisStroke);
|
|
||||||
g.fill(arrow);
|
|
||||||
g.draw(arrow);
|
|
||||||
g.setStroke(saveStroke);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isActive) {
|
|
||||||
Stroke saveStroke = g.getStroke();
|
|
||||||
g.setPaint(hoveredColor);
|
|
||||||
g.setStroke(hoverStroke);
|
|
||||||
g.fill(arrow);
|
|
||||||
g.draw(arrow);
|
|
||||||
g.setStroke(saveStroke);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isSelected) {
|
|
||||||
Stroke saveStroke = g.getStroke();
|
|
||||||
g.setPaint(selectedColor);
|
|
||||||
g.setStroke(selectedStroke);
|
|
||||||
g.fill(arrow);
|
|
||||||
g.draw(arrow);
|
|
||||||
g.setStroke(saveStroke);
|
|
||||||
}
|
|
||||||
|
|
||||||
g.setPaint(arrowFillPaint);
|
|
||||||
g.fill(arrow);
|
|
||||||
g.setPaint(arrowDrawPaint);
|
|
||||||
g.draw(arrow);
|
|
||||||
}
|
|
||||||
|
|
||||||
// restore paint and stroke
|
|
||||||
if (new_stroke != null) {
|
|
||||||
g.setStroke(old_stroke);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// restore old paint
|
|
||||||
g.setPaint(oldPaint);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Paint oldPaint = g.getPaint();
|
||||||
|
|
||||||
|
// get Paints for filling and drawing
|
||||||
|
// (filling is done first so that drawing and label use the same Paint)
|
||||||
|
BasicStroke hoverStroke = getHoveredPathStroke(e, scale);
|
||||||
|
BasicStroke focusedStroke = getFocusedPathStroke(e, scale);
|
||||||
|
BasicStroke selectedStroke = getSelectedStroke(e, scale);
|
||||||
|
BasicStroke selectedAccentStroke = getSelectedAccentStroke(e, scale);
|
||||||
|
BasicStroke empahsisStroke = getEmphasisStroke(e, scale);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Fill
|
||||||
|
//
|
||||||
|
Paint fillPaint = rc.getEdgeFillPaintTransformer().apply(e);
|
||||||
|
if (fillPaint != null) {
|
||||||
|
// basic shape
|
||||||
|
g.setPaint(fillPaint);
|
||||||
|
g.fill(edgeShape);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isEmphasized) {
|
||||||
|
Stroke saveStroke = g.getStroke();
|
||||||
|
g.setPaint(fillPaint);
|
||||||
|
g.setStroke(empahsisStroke);
|
||||||
|
g.fill(edgeShape);
|
||||||
|
g.setStroke(saveStroke);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isInHoveredPath) {
|
||||||
|
Stroke saveStroke = g.getStroke();
|
||||||
|
g.setPaint(hoveredColor);
|
||||||
|
g.setStroke(hoverStroke);
|
||||||
|
g.fill(edgeShape);
|
||||||
|
g.setStroke(saveStroke);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isInFocusedPath) {
|
||||||
|
Stroke saveStroke = g.getStroke();
|
||||||
|
g.setPaint(focusedColor);
|
||||||
|
g.setStroke(focusedStroke);
|
||||||
|
g.fill(edgeShape);
|
||||||
|
g.setStroke(saveStroke);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isSelected) {
|
||||||
|
Stroke saveStroke = g.getStroke();
|
||||||
|
g.setPaint(selectedColor);
|
||||||
|
g.setStroke(selectedStroke);
|
||||||
|
g.fill(edgeShape);
|
||||||
|
g.setStroke(saveStroke);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Draw
|
||||||
|
//
|
||||||
|
Paint drawPaint = rc.getEdgeDrawPaintTransformer().apply(e);
|
||||||
|
if (drawPaint != null) {
|
||||||
|
// basic shape
|
||||||
|
g.setPaint(drawPaint);
|
||||||
|
g.draw(edgeShape);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isEmphasized) {
|
||||||
|
Stroke saveStroke = g.getStroke();
|
||||||
|
g.setPaint(drawPaint);
|
||||||
|
g.setStroke(empahsisStroke);
|
||||||
|
g.draw(edgeShape);
|
||||||
|
g.setStroke(saveStroke);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isInHoveredPath) {
|
||||||
|
Stroke saveStroke = g.getStroke();
|
||||||
|
g.setPaint(hoveredColor);
|
||||||
|
g.setStroke(hoverStroke);
|
||||||
|
g.draw(edgeShape);
|
||||||
|
g.setStroke(saveStroke);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isInFocusedPath) {
|
||||||
|
Stroke saveStroke = g.getStroke();
|
||||||
|
g.setPaint(focusedColor);
|
||||||
|
g.setStroke(focusedStroke);
|
||||||
|
g.draw(edgeShape);
|
||||||
|
g.setStroke(saveStroke);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isSelected) {
|
||||||
|
Stroke saveStroke = g.getStroke();
|
||||||
|
|
||||||
|
g.setPaint(selectedAccentColor);
|
||||||
|
g.setStroke(selectedAccentStroke);
|
||||||
|
g.draw(edgeShape);
|
||||||
|
|
||||||
|
g.setPaint(selectedColor);
|
||||||
|
g.setStroke(selectedStroke);
|
||||||
|
g.draw(edgeShape);
|
||||||
|
g.setStroke(saveStroke);
|
||||||
|
}
|
||||||
|
|
||||||
|
// debug - draw a box around the edge
|
||||||
|
//Rectangle shapeBounds = edgeShape.getBounds();
|
||||||
|
//g.setPaint(Color.ORANGE);
|
||||||
|
//g.draw(shapeBounds);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Arrow Head
|
||||||
|
//
|
||||||
|
Predicate<Context<Graph<V, E>, E>> predicate = rc.getEdgeArrowPredicate();
|
||||||
|
boolean drawArrow = predicate.apply(context);
|
||||||
|
if (!drawArrow) {
|
||||||
|
g.setPaint(oldPaint);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Stroke arrowStroke = rc.getEdgeArrowStrokeTransformer().apply(e);
|
||||||
|
Stroke oldArrowStroke = g.getStroke();
|
||||||
|
if (arrowStroke != null) {
|
||||||
|
g.setStroke(arrowStroke);
|
||||||
|
}
|
||||||
|
|
||||||
|
Shape vs2 = getVertexShapeForArrow(rc, layout, v2); // end vertex
|
||||||
|
boolean arrowHit = vt.transform(vs2).intersects(deviceRectangle);
|
||||||
|
if (!arrowHit) {
|
||||||
|
g.setPaint(oldPaint);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
EdgeArrowRenderingSupport<V, E> arrowRenderingSupport =
|
||||||
|
new BasicEdgeArrowRenderingSupport<>();
|
||||||
|
AffineTransform at = arrowRenderingSupport.getArrowTransform(rc, edgeShape, vs2);
|
||||||
|
if (at == null) {
|
||||||
|
g.setPaint(oldPaint);
|
||||||
|
g.setStroke(oldArrowStroke);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Paint arrowFillPaint = rc.getArrowFillPaintTransformer().apply(e);
|
||||||
|
Paint arrowDrawPaint = rc.getArrowDrawPaintTransformer().apply(e);
|
||||||
|
Shape arrow = rc.getEdgeArrowTransformer().apply(context);
|
||||||
|
arrow = scaleArrowForBetterVisibility(rc, arrow);
|
||||||
|
arrow = at.createTransformedShape(arrow);
|
||||||
|
|
||||||
|
// basic shape
|
||||||
|
g.setPaint(arrowFillPaint);
|
||||||
|
g.fill(arrow);
|
||||||
|
g.setPaint(arrowDrawPaint);
|
||||||
|
g.draw(arrow);
|
||||||
|
|
||||||
|
if (isEmphasized) {
|
||||||
|
Stroke saveStroke = g.getStroke();
|
||||||
|
g.setPaint(arrowDrawPaint);
|
||||||
|
g.setStroke(empahsisStroke);
|
||||||
|
g.fill(arrow);
|
||||||
|
g.draw(arrow);
|
||||||
|
g.setStroke(saveStroke);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isInHoveredPath) {
|
||||||
|
Stroke saveStroke = g.getStroke();
|
||||||
|
g.setPaint(hoveredColor);
|
||||||
|
g.setStroke(hoverStroke);
|
||||||
|
g.fill(arrow);
|
||||||
|
g.draw(arrow);
|
||||||
|
g.setStroke(saveStroke);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isInFocusedPath) {
|
||||||
|
Stroke saveStroke = g.getStroke();
|
||||||
|
g.setPaint(focusedColor);
|
||||||
|
g.setStroke(focusedStroke);
|
||||||
|
g.draw(edgeShape);
|
||||||
|
g.setStroke(saveStroke);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isSelected) {
|
||||||
|
Stroke saveStroke = g.getStroke();
|
||||||
|
g.setPaint(selectedColor);
|
||||||
|
g.setStroke(selectedStroke);
|
||||||
|
g.fill(arrow);
|
||||||
|
g.draw(arrow);
|
||||||
|
g.setStroke(saveStroke);
|
||||||
|
}
|
||||||
|
|
||||||
|
g.setStroke(oldArrowStroke);
|
||||||
|
g.setPaint(oldPaint);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Shape getVertexShapeForArrow(RenderContext<V, E> rc, Layout<V, E> layout, V v) {
|
protected Shape getVertexShapeForArrow(RenderContext<V, E> rc, Layout<V, E> layout, V v) {
|
||||||
|
@ -355,17 +415,27 @@ public abstract class VisualEdgeRenderer<V extends VisualVertex, E extends Visua
|
||||||
public abstract Shape getEdgeShape(RenderContext<V, E> rc, Graph<V, E> graph, E e, float x1,
|
public abstract Shape getEdgeShape(RenderContext<V, E> rc, Graph<V, E> graph, E e, float x1,
|
||||||
float y1, float x2, float y2, boolean isLoop, Shape vertexShape);
|
float y1, float x2, float y2, boolean isLoop, Shape vertexShape);
|
||||||
|
|
||||||
private BasicStroke getHoveredStroke(E e, float scale) {
|
private BasicStroke getHoveredPathStroke(E e, float scale) {
|
||||||
float width = HOVERED_STROKE_WIDTH / (float) Math.pow(scale, .80);
|
float width = HOVERED_PATH_STROKE_WIDTH / (float) Math.pow(scale, .80);
|
||||||
return new BasicStroke(width, BasicStroke.CAP_ROUND, BasicStroke.JOIN_BEVEL, 0f,
|
return new BasicStroke(width, BasicStroke.CAP_ROUND, BasicStroke.JOIN_BEVEL, 0f,
|
||||||
new float[] { width * 1, width * 2 }, width * 3 * dashingPatternOffset);
|
new float[] { width * 1, width * 2 }, width * 3 * dashingPatternOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private BasicStroke getFocusedPathStroke(E e, float scale) {
|
||||||
|
float width = FOCUSED_PATH_STROKE_WIDTH / (float) Math.pow(scale, .80);
|
||||||
|
return new BasicStroke(width);
|
||||||
|
}
|
||||||
|
|
||||||
private BasicStroke getSelectedStroke(E e, float scale) {
|
private BasicStroke getSelectedStroke(E e, float scale) {
|
||||||
float width = SELECTED_STROKE_WIDTH / (float) Math.pow(scale, .80);
|
float width = SELECTED_STROKE_WIDTH / (float) Math.pow(scale, .80);
|
||||||
return new BasicStroke(width);
|
return new BasicStroke(width);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private BasicStroke getSelectedAccentStroke(E e, float scale) {
|
||||||
|
float width = (SELECTED_STROKE_WIDTH + 2) / (float) Math.pow(scale, .80);
|
||||||
|
return new BasicStroke(width);
|
||||||
|
}
|
||||||
|
|
||||||
private BasicStroke getEmphasisStroke(E e, float scale) {
|
private BasicStroke getEmphasisStroke(E e, float scale) {
|
||||||
double emphasisRatio = e.getEmphasis(); // this value is 0 when no emphasis
|
double emphasisRatio = e.getEmphasis(); // this value is 0 when no emphasis
|
||||||
float fullEmphasis = EMPHASIZED_STOKE_WIDTH;
|
float fullEmphasis = EMPHASIZED_STOKE_WIDTH;
|
||||||
|
|
|
@ -27,6 +27,8 @@ import ghidra.graph.viewer.VisualVertex;
|
||||||
* A renderer designed to override default edge rendering to NOT paint emphasizing effects. We
|
* A renderer designed to override default edge rendering to NOT paint emphasizing effects. We
|
||||||
* do this because space is limited in the satellite and because this rendering can take excess
|
* do this because space is limited in the satellite and because this rendering can take excess
|
||||||
* processing time.
|
* processing time.
|
||||||
|
* @param <V> the vertex type
|
||||||
|
* @param <E> the edge type
|
||||||
*/
|
*/
|
||||||
public class VisualGraphEdgeSatelliteRenderer<V extends VisualVertex, E extends VisualEdge<V>>
|
public class VisualGraphEdgeSatelliteRenderer<V extends VisualVertex, E extends VisualEdge<V>>
|
||||||
extends VisualEdgeRenderer<V, E> {
|
extends VisualEdgeRenderer<V, E> {
|
||||||
|
@ -38,7 +40,12 @@ public class VisualGraphEdgeSatelliteRenderer<V extends VisualVertex, E extends
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean isInActivePath(E e) {
|
protected boolean isInHoveredVertexPath(E e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isInFocusedVertexPath(E e) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -268,6 +268,9 @@ public class VisualGraphPathHighlighter<V extends VisualVertex, E extends Visual
|
||||||
|
|
||||||
public void setVertexHoverMode(PathHighlightMode mode) {
|
public void setVertexHoverMode(PathHighlightMode mode) {
|
||||||
this.vertexHoverMode = Objects.requireNonNull(mode);
|
this.vertexHoverMode = Objects.requireNonNull(mode);
|
||||||
|
if (vertexHoverMode == PathHighlightMode.OFF) {
|
||||||
|
clearHoveredEdgesSwing();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setHoveredVertex(V hoveredVertex) {
|
public void setHoveredVertex(V hoveredVertex) {
|
||||||
|
@ -362,13 +365,13 @@ public class VisualGraphPathHighlighter<V extends VisualVertex, E extends Visual
|
||||||
|
|
||||||
private void clearHoveredEdgesSwing() {
|
private void clearHoveredEdgesSwing() {
|
||||||
for (E edge : graph.getEdges()) {
|
for (E edge : graph.getEdges()) {
|
||||||
edge.setInActivePath(false);
|
edge.setInHoveredVertexPath(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void clearFocusedEdgesSwing() {
|
private void clearFocusedEdgesSwing() {
|
||||||
for (E edge : graph.getEdges()) {
|
for (E edge : graph.getEdges()) {
|
||||||
edge.setSelected(false);
|
edge.setInFocusedVertexPath(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -498,25 +501,25 @@ public class VisualGraphPathHighlighter<V extends VisualVertex, E extends Visual
|
||||||
private void setInFocusedEdges(V vertex) {
|
private void setInFocusedEdges(V vertex) {
|
||||||
|
|
||||||
Supplier<Set<E>> supplier = () -> getReverseFlowEdgesForVertexAsync(vertex);
|
Supplier<Set<E>> supplier = () -> getReverseFlowEdgesForVertexAsync(vertex);
|
||||||
focusRunManager.runNow(new SelectRunnable(supplier), null);
|
focusRunManager.runNow(new SetFocusedEdgesRunnable(supplier), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setOutFocusedEdgesSwing(V vertex) {
|
private void setOutFocusedEdgesSwing(V vertex) {
|
||||||
|
|
||||||
Supplier<Set<E>> supplier = () -> getForwardFlowEdgesForVertexAsync(vertex);
|
Supplier<Set<E>> supplier = () -> getForwardFlowEdgesForVertexAsync(vertex);
|
||||||
focusRunManager.runNow(new SelectRunnable(supplier), null);
|
focusRunManager.runNow(new SetFocusedEdgesRunnable(supplier), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setForwardScopedFlowFocusedEdgesSwing(V vertex) {
|
private void setForwardScopedFlowFocusedEdgesSwing(V vertex) {
|
||||||
|
|
||||||
Supplier<Set<E>> supplier = () -> getForwardScopedFlowEdgesForVertexAsync(vertex);
|
Supplier<Set<E>> supplier = () -> getForwardScopedFlowEdgesForVertexAsync(vertex);
|
||||||
focusRunManager.runNow(new SelectRunnable(supplier), null);
|
focusRunManager.runNow(new SetFocusedEdgesRunnable(supplier), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setReverseScopedFlowFocusedEdgesSwing(V vertex) {
|
private void setReverseScopedFlowFocusedEdgesSwing(V vertex) {
|
||||||
|
|
||||||
Supplier<Set<E>> supplier = () -> getReverseScopedFlowEdgesForVertexAsync(vertex);
|
Supplier<Set<E>> supplier = () -> getReverseScopedFlowEdgesForVertexAsync(vertex);
|
||||||
focusRunManager.runNow(new SelectRunnable(supplier), null);
|
focusRunManager.runNow(new SetFocusedEdgesRunnable(supplier), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setInOutFocusedEdgesSwing(V vertex) {
|
private void setInOutFocusedEdgesSwing(V vertex) {
|
||||||
|
@ -525,46 +528,46 @@ public class VisualGraphPathHighlighter<V extends VisualVertex, E extends Visual
|
||||||
// Select ins and outs, one after the other.
|
// Select ins and outs, one after the other.
|
||||||
//
|
//
|
||||||
Supplier<Set<E>> inSupplier = () -> getReverseFlowEdgesForVertexAsync(vertex);
|
Supplier<Set<E>> inSupplier = () -> getReverseFlowEdgesForVertexAsync(vertex);
|
||||||
focusRunManager.runNow(new SelectRunnable(inSupplier), null);
|
focusRunManager.runNow(new SetFocusedEdgesRunnable(inSupplier), null);
|
||||||
|
|
||||||
Supplier<Set<E>> outSupplier = () -> getForwardFlowEdgesForVertexAsync(vertex);
|
Supplier<Set<E>> outSupplier = () -> getForwardFlowEdgesForVertexAsync(vertex);
|
||||||
focusRunManager.runNext(new SelectRunnable(outSupplier), null);
|
focusRunManager.runNext(new SetFocusedEdgesRunnable(outSupplier), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setVertexCycleFocusedEdgesSwing(V vertex) {
|
private void setVertexCycleFocusedEdgesSwing(V vertex) {
|
||||||
|
|
||||||
Supplier<Set<E>> supplier = () -> getCircuitEdgesAsync(vertex);
|
Supplier<Set<E>> supplier = () -> getCircuitEdgesAsync(vertex);
|
||||||
focusRunManager.runNow(new SelectRunnable(supplier), null);
|
focusRunManager.runNow(new SetFocusedEdgesRunnable(supplier), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setAllCycleFocusedEdgesSwing() {
|
private void setAllCycleFocusedEdgesSwing() {
|
||||||
|
|
||||||
Supplier<Set<E>> supplier = () -> getAllCircuitFlowEdgesAsync();
|
Supplier<Set<E>> supplier = () -> getAllCircuitFlowEdgesAsync();
|
||||||
focusRunManager.runNow(new SelectRunnable(supplier), null);
|
focusRunManager.runNow(new SetFocusedEdgesRunnable(supplier), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setInHoveredEdgesSwing(V vertex) {
|
private void setInHoveredEdgesSwing(V vertex) {
|
||||||
|
|
||||||
Supplier<Set<E>> supplier = () -> getReverseFlowEdgesForVertexAsync(vertex);
|
Supplier<Set<E>> supplier = () -> getReverseFlowEdgesForVertexAsync(vertex);
|
||||||
hoverRunManager.runNow(new HoverRunnable(supplier), null);
|
hoverRunManager.runNow(new SetHoveredEdgesRunnable(supplier), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setOutHoveredEdgesSwing(V vertex) {
|
private void setOutHoveredEdgesSwing(V vertex) {
|
||||||
|
|
||||||
Supplier<Set<E>> supplier = () -> getForwardFlowEdgesForVertexAsync(vertex);
|
Supplier<Set<E>> supplier = () -> getForwardFlowEdgesForVertexAsync(vertex);
|
||||||
hoverRunManager.runNow(new HoverRunnable(supplier), null);
|
hoverRunManager.runNow(new SetHoveredEdgesRunnable(supplier), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setForwardScopedFlowHoveredEdgesSwing(V vertex) {
|
private void setForwardScopedFlowHoveredEdgesSwing(V vertex) {
|
||||||
|
|
||||||
Supplier<Set<E>> supplier = () -> getForwardScopedFlowEdgesForVertexAsync(vertex);
|
Supplier<Set<E>> supplier = () -> getForwardScopedFlowEdgesForVertexAsync(vertex);
|
||||||
hoverRunManager.runNow(new HoverRunnable(supplier), null);
|
hoverRunManager.runNow(new SetHoveredEdgesRunnable(supplier), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setReverseScopedFlowHoveredEdgesSwing(V vertex) {
|
private void setReverseScopedFlowHoveredEdgesSwing(V vertex) {
|
||||||
|
|
||||||
Supplier<Set<E>> supplier = () -> getReverseScopedFlowEdgesForVertexAsync(vertex);
|
Supplier<Set<E>> supplier = () -> getReverseScopedFlowEdgesForVertexAsync(vertex);
|
||||||
hoverRunManager.runNow(new HoverRunnable(supplier), null);
|
hoverRunManager.runNow(new SetHoveredEdgesRunnable(supplier), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setInOutHoveredEdgesSwing(V vertex) {
|
private void setInOutHoveredEdgesSwing(V vertex) {
|
||||||
|
@ -573,32 +576,32 @@ public class VisualGraphPathHighlighter<V extends VisualVertex, E extends Visual
|
||||||
// Select ins and outs, one after the other.
|
// Select ins and outs, one after the other.
|
||||||
//
|
//
|
||||||
Supplier<Set<E>> inSupplier = () -> getReverseFlowEdgesForVertexAsync(vertex);
|
Supplier<Set<E>> inSupplier = () -> getReverseFlowEdgesForVertexAsync(vertex);
|
||||||
hoverRunManager.runNow(new HoverRunnable(inSupplier), null);
|
hoverRunManager.runNow(new SetHoveredEdgesRunnable(inSupplier), null);
|
||||||
|
|
||||||
Supplier<Set<E>> outSupplier = () -> getForwardFlowEdgesForVertexAsync(vertex);
|
Supplier<Set<E>> outSupplier = () -> getForwardFlowEdgesForVertexAsync(vertex);
|
||||||
hoverRunManager.runNext(new HoverRunnable(outSupplier), null);
|
hoverRunManager.runNext(new SetHoveredEdgesRunnable(outSupplier), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setVertexCycleHoveredEdgesSwing(V vertex) {
|
private void setVertexCycleHoveredEdgesSwing(V vertex) {
|
||||||
|
|
||||||
Supplier<Set<E>> supplier = () -> getCircuitEdgesAsync(vertex);
|
Supplier<Set<E>> supplier = () -> getCircuitEdgesAsync(vertex);
|
||||||
hoverRunManager.runNow(new HoverRunnable(supplier), null);
|
hoverRunManager.runNow(new SetHoveredEdgesRunnable(supplier), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setVertexToVertexPathHoveredEdgesSwing(V start, V end) {
|
private void setVertexToVertexPathHoveredEdgesSwing(V start, V end) {
|
||||||
|
|
||||||
Callback callback = () -> calculatePathsBetweenVerticesAsync(start, end);
|
Callback callback = () -> calculatePathsBetweenVerticesAsync(start, end);
|
||||||
focusRunManager.runNow(new SlowHoverRunnable(callback), null);
|
focusRunManager.runNow(new SlowSetHoveredEdgesRunnable(callback), null);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void selectSwing(Collection<E> edges) {
|
private void setInFocusedPathOnSwing(Collection<E> edges) {
|
||||||
edges.forEach(e -> e.setSelected(true));
|
edges.forEach(e -> e.setInFocusedVertexPath(true));
|
||||||
listener.pathHighlightChanged(false);
|
listener.pathHighlightChanged(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void activateSwing(Collection<E> edges) {
|
private void setInHoverPathOnSwing(Collection<E> edges) {
|
||||||
edges.forEach(e -> e.setInActivePath(true));
|
edges.forEach(e -> e.setInHoveredVertexPath(true));
|
||||||
listener.pathHighlightChanged(true);
|
listener.pathHighlightChanged(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -833,7 +836,7 @@ public class VisualGraphPathHighlighter<V extends VisualVertex, E extends Visual
|
||||||
CallbackAccumulator<List<V>> accumulator = new CallbackAccumulator<>(path -> {
|
CallbackAccumulator<List<V>> accumulator = new CallbackAccumulator<>(path -> {
|
||||||
|
|
||||||
Collection<E> edges = pathToEdgesAsync(path);
|
Collection<E> edges = pathToEdgesAsync(path);
|
||||||
SystemUtilities.runSwingLater(() -> activateSwing(edges));
|
SystemUtilities.runSwingLater(() -> setInHoverPathOnSwing(edges));
|
||||||
});
|
});
|
||||||
|
|
||||||
TaskMonitor timeoutMonitor = TimeoutTaskMonitor.timeoutIn(ALGORITHM_TIMEOUT,
|
TaskMonitor timeoutMonitor = TimeoutTaskMonitor.timeoutIn(ALGORITHM_TIMEOUT,
|
||||||
|
@ -890,12 +893,12 @@ public class VisualGraphPathHighlighter<V extends VisualVertex, E extends Visual
|
||||||
* A class to handle off-loading the calculation of edges to be hovered. The results will
|
* A class to handle off-loading the calculation of edges to be hovered. The results will
|
||||||
* then be used to update the UI.
|
* then be used to update the UI.
|
||||||
*/
|
*/
|
||||||
private class HoverRunnable implements SwingRunnable {
|
private class SetHoveredEdgesRunnable implements SwingRunnable {
|
||||||
|
|
||||||
private Supplier<Set<E>> edgeSupplier;
|
private Supplier<Set<E>> edgeSupplier;
|
||||||
private Set<E> edges;
|
private Set<E> edges;
|
||||||
|
|
||||||
HoverRunnable(Supplier<Set<E>> edgeSupplier) {
|
SetHoveredEdgesRunnable(Supplier<Set<E>> edgeSupplier) {
|
||||||
this.edgeSupplier = edgeSupplier;
|
this.edgeSupplier = edgeSupplier;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -915,20 +918,20 @@ public class VisualGraphPathHighlighter<V extends VisualVertex, E extends Visual
|
||||||
if (isCancelled) {
|
if (isCancelled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
activateSwing(edges);
|
setInHoverPathOnSwing(edges);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A class to handle off-loading the calculation of edges to be focused/selected.
|
* A class to handle off-loading the calculation of edges to be focused.
|
||||||
* The results will then be used to update the UI.
|
* The results will then be used to update the UI.
|
||||||
*/
|
*/
|
||||||
private class SelectRunnable implements SwingRunnable {
|
private class SetFocusedEdgesRunnable implements SwingRunnable {
|
||||||
|
|
||||||
private Supplier<Set<E>> edgeSupplier;
|
private Supplier<Set<E>> edgeSupplier;
|
||||||
private Set<E> edges;
|
private Set<E> edges;
|
||||||
|
|
||||||
SelectRunnable(Supplier<Set<E>> edgeSupplier) {
|
SetFocusedEdgesRunnable(Supplier<Set<E>> edgeSupplier) {
|
||||||
this.edgeSupplier = edgeSupplier;
|
this.edgeSupplier = edgeSupplier;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -948,7 +951,7 @@ public class VisualGraphPathHighlighter<V extends VisualVertex, E extends Visual
|
||||||
if (isCancelled) {
|
if (isCancelled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
selectSwing(edges);
|
setInFocusedPathOnSwing(edges);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -956,11 +959,11 @@ public class VisualGraphPathHighlighter<V extends VisualVertex, E extends Visual
|
||||||
* A class meant to run in the hover RunManager that is slow or open-ended. Work will
|
* A class meant to run in the hover RunManager that is slow or open-ended. Work will
|
||||||
* be performed as long as possible, updating results along the way.
|
* be performed as long as possible, updating results along the way.
|
||||||
*/
|
*/
|
||||||
private class SlowHoverRunnable implements MonitoredRunnable {
|
private class SlowSetHoveredEdgesRunnable implements MonitoredRunnable {
|
||||||
|
|
||||||
private Callback callback;
|
private Callback callback;
|
||||||
|
|
||||||
SlowHoverRunnable(Callback callback) {
|
SlowSetHoveredEdgesRunnable(Callback callback) {
|
||||||
this.callback = callback;
|
this.callback = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ import edu.uci.ics.jung.visualization.control.AbstractGraphMousePlugin;
|
||||||
import ghidra.graph.VisualGraph;
|
import ghidra.graph.VisualGraph;
|
||||||
import ghidra.graph.viewer.*;
|
import ghidra.graph.viewer.*;
|
||||||
import ghidra.graph.viewer.edge.VisualGraphPathHighlighter;
|
import ghidra.graph.viewer.edge.VisualGraphPathHighlighter;
|
||||||
|
import ghidra.util.task.SwingUpdateManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A mouse plugin to handle vertex hovers, to include animating paths in the graph, based
|
* A mouse plugin to handle vertex hovers, to include animating paths in the graph, based
|
||||||
|
@ -45,6 +46,8 @@ public class VisualGraphHoverMousePlugin<V extends VisualVertex, E extends Visua
|
||||||
private final VisualizationViewer<V, E> sourceViewer;
|
private final VisualizationViewer<V, E> sourceViewer;
|
||||||
private final VisualizationViewer<V, E> otherViewer;
|
private final VisualizationViewer<V, E> otherViewer;
|
||||||
|
|
||||||
|
private SwingUpdateManager mouseHoverUpdater = new SwingUpdateManager(this::updateMouseHovers);
|
||||||
|
private MouseEvent lastMouseEvent;
|
||||||
private V hoveredVertex;
|
private V hoveredVertex;
|
||||||
|
|
||||||
public VisualGraphHoverMousePlugin(GraphComponent<V, E, ?> graphComponent,
|
public VisualGraphHoverMousePlugin(GraphComponent<V, E, ?> graphComponent,
|
||||||
|
@ -60,16 +63,23 @@ public class VisualGraphHoverMousePlugin<V extends VisualVertex, E extends Visua
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void mouseMoved(MouseEvent e) {
|
public void mouseMoved(MouseEvent e) {
|
||||||
updateMouseHovers(e);
|
lastMouseEvent = e;
|
||||||
|
mouseHoverUpdater.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateMouseHovers(MouseEvent e) {
|
private void updateMouseHovers() {
|
||||||
if (graphComponent.isUninitialized()) {
|
if (graphComponent.isUninitialized()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
GraphViewer<V, E> viewer = getGraphViewer(e);
|
PathHighlightMode hoverMode = graphComponent.getVertexHoverPathHighlightMode();
|
||||||
V newHoveredVertex = GraphViewerUtils.getVertexFromPointInViewSpace(viewer, e.getPoint());
|
if (hoverMode == PathHighlightMode.OFF) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GraphViewer<V, E> viewer = getGraphViewer(lastMouseEvent);
|
||||||
|
V newHoveredVertex =
|
||||||
|
GraphViewerUtils.getVertexFromPointInViewSpace(viewer, lastMouseEvent.getPoint());
|
||||||
if (newHoveredVertex == hoveredVertex) {
|
if (newHoveredVertex == hoveredVertex) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -123,7 +133,9 @@ public class VisualGraphHoverMousePlugin<V extends VisualVertex, E extends Visua
|
||||||
if (e.isPopupTrigger()) {
|
if (e.isPopupTrigger()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
updateMouseHovers(e);
|
|
||||||
|
lastMouseEvent = e;
|
||||||
|
updateMouseHovers();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -140,4 +152,9 @@ public class VisualGraphHoverMousePlugin<V extends VisualVertex, E extends Visua
|
||||||
public void mouseClicked(MouseEvent e) {
|
public void mouseClicked(MouseEvent e) {
|
||||||
// handled by dragged and released
|
// handled by dragged and released
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dispose() {
|
||||||
|
mouseHoverUpdater.dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,4 +108,11 @@ public interface VisualGraphMousePlugin<V extends VisualVertex, E extends Visual
|
||||||
VisualGraphViewUpdater<V, E> updater = viewer.getViewUpdater();
|
VisualGraphViewUpdater<V, E> updater = viewer.getViewUpdater();
|
||||||
return updater;
|
return updater;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signals to perform any cleanup when this plugin is going away
|
||||||
|
*/
|
||||||
|
public default void dispose() {
|
||||||
|
// stub
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,6 +95,11 @@ public class VisualGraphPluggableGraphMouse<V extends VisualVertex, E extends Vi
|
||||||
}
|
}
|
||||||
|
|
||||||
public void dispose() {
|
public void dispose() {
|
||||||
|
for (GraphMousePlugin mp : mousePlugins) {
|
||||||
|
if (mp instanceof VisualGraphMousePlugin) {
|
||||||
|
((VisualGraphMousePlugin<?, ?>) mp).dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
mousePlugins.clear();
|
mousePlugins.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -675,12 +675,12 @@ public class VisualGraphPathHighlighterTest extends AbstractVisualGraphTest {
|
||||||
nonHoveredEdges.removeAll(expectedEdges);
|
nonHoveredEdges.removeAll(expectedEdges);
|
||||||
|
|
||||||
for (TestEdge e : expectedEdges) {
|
for (TestEdge e : expectedEdges) {
|
||||||
boolean isHovered = swing(() -> e.isInActivePath());
|
boolean isHovered = swing(() -> e.isInHoveredVertexPath());
|
||||||
assertTrue("Edge was not hovered: " + e, isHovered);
|
assertTrue("Edge was not hovered: " + e, isHovered);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (TestEdge e : nonHoveredEdges) {
|
for (TestEdge e : nonHoveredEdges) {
|
||||||
boolean isHovered = swing(() -> e.isInActivePath());
|
boolean isHovered = swing(() -> e.isInHoveredVertexPath());
|
||||||
assertFalse("Edge hovered when it should not have been: " + e, isHovered);
|
assertFalse("Edge hovered when it should not have been: " + e, isHovered);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -694,7 +694,7 @@ public class VisualGraphPathHighlighterTest extends AbstractVisualGraphTest {
|
||||||
|
|
||||||
private void assertNotHovered(TestEdge... edges) {
|
private void assertNotHovered(TestEdge... edges) {
|
||||||
for (TestEdge e : edges) {
|
for (TestEdge e : edges) {
|
||||||
boolean isHovered = swing(() -> e.isInActivePath());
|
boolean isHovered = swing(() -> e.isInHoveredVertexPath());
|
||||||
assertFalse("Edge should not have been hovered: " + e, isHovered);
|
assertFalse("Edge should not have been hovered: " + e, isHovered);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue