Line | Hits | Source |
---|---|---|
1 | /* | |
2 | * Copyright (c) 2003, the JUNG Project and the Regents of the University of | |
3 | * California All rights reserved. | |
4 | * | |
5 | * This software is open-source under the BSD license; see either "license.txt" | |
6 | * or http://jung.sourceforge.net/license.txt for a description. | |
7 | * | |
8 | */ | |
9 | package edu.uci.ics.jung.visualization.control; | |
10 | ||
11 | import java.awt.Color; | |
12 | import java.awt.Graphics; | |
13 | import java.awt.Graphics2D; | |
14 | import java.awt.Shape; | |
15 | import java.awt.event.MouseEvent; | |
16 | import java.awt.event.MouseListener; | |
17 | import java.awt.event.MouseMotionListener; | |
18 | import java.awt.geom.AffineTransform; | |
19 | import java.awt.geom.CubicCurve2D; | |
20 | import java.awt.geom.Point2D; | |
21 | import java.util.Iterator; | |
22 | ||
23 | import edu.uci.ics.jung.graph.Graph; | |
24 | import edu.uci.ics.jung.graph.Vertex; | |
25 | import edu.uci.ics.jung.graph.impl.DirectedSparseEdge; | |
26 | import edu.uci.ics.jung.graph.impl.SparseVertex; | |
27 | import edu.uci.ics.jung.graph.impl.UndirectedSparseEdge; | |
28 | import edu.uci.ics.jung.visualization.ArrowFactory; | |
29 | import edu.uci.ics.jung.visualization.Layout; | |
30 | import edu.uci.ics.jung.visualization.PickSupport; | |
31 | import edu.uci.ics.jung.visualization.SettableVertexLocationFunction; | |
32 | import edu.uci.ics.jung.visualization.VisualizationViewer; | |
33 | import edu.uci.ics.jung.visualization.VisualizationViewer.Paintable; | |
34 | ||
35 | /** | |
36 | * A plugin that can create vertices, undirected edges, and directed edges | |
37 | * using mouse gestures. | |
38 | * | |
39 | * @author Tom Nelson - RABA Technologies | |
40 | * | |
41 | */ | |
42 | public class EditingGraphMousePlugin extends AbstractGraphMousePlugin implements | |
43 | MouseListener, MouseMotionListener { | |
44 | ||
45 | SettableVertexLocationFunction vertexLocations; | |
46 | Vertex startVertex; | |
47 | Point2D down; | |
48 | ||
49 | 0 | CubicCurve2D rawEdge = new CubicCurve2D.Float(); |
50 | Shape edgeShape; | |
51 | Shape rawArrowShape; | |
52 | Shape arrowShape; | |
53 | Paintable edgePaintable; | |
54 | Paintable arrowPaintable; | |
55 | boolean edgeIsDirected; | |
56 | ||
57 | public EditingGraphMousePlugin() { | |
58 | 0 | this(MouseEvent.BUTTON1_MASK); |
59 | 0 | } |
60 | ||
61 | /** | |
62 | * create instance and prepare shapes for visual effects | |
63 | * @param modifiers | |
64 | */ | |
65 | public EditingGraphMousePlugin(int modifiers) { | |
66 | 0 | super(modifiers); |
67 | 0 | rawEdge.setCurve(0.0f, 0.0f, 0.33f, 100, .66f, -50, |
68 | 1.0f, 0.0f); | |
69 | 0 | rawArrowShape = ArrowFactory.getNotchedArrow(20, 16, 8); |
70 | 0 | edgePaintable = new EdgePaintable(); |
71 | 0 | arrowPaintable = new ArrowPaintable(); |
72 | 0 | } |
73 | ||
74 | /** | |
75 | * sets the vertex locations. Needed to place new vertices | |
76 | * @param vertexLocations | |
77 | */ | |
78 | public void setVertexLocations(SettableVertexLocationFunction vertexLocations) { | |
79 | 0 | this.vertexLocations = vertexLocations; |
80 | 0 | } |
81 | ||
82 | /** | |
83 | * overrided to be more flexible, and pass events with | |
84 | * key combinations. The default responds to both ButtonOne | |
85 | * and ButtonOne+Shift | |
86 | */ | |
87 | public boolean checkModifiers(MouseEvent e) { | |
88 | 0 | return (e.getModifiers() & modifiers) != 0; |
89 | } | |
90 | ||
91 | /** | |
92 | * If the mouse is pressed in an empty area, create a new vertex there. | |
93 | * If the mouse is pressed on an existing vertex, prepare to create | |
94 | * an edge from that vertex to another | |
95 | */ | |
96 | public void mousePressed(MouseEvent e) { | |
97 | 0 | if(checkModifiers(e)) { |
98 | 0 | final VisualizationViewer vv = |
99 | (VisualizationViewer)e.getSource(); | |
100 | 0 | final Point2D p = vv.inverseViewTransform(e.getPoint()); |
101 | 0 | PickSupport pickSupport = vv.getPickSupport(); |
102 | 0 | if(pickSupport != null) { |
103 | 0 | final Vertex vertex = pickSupport.getVertex(p.getX(), p.getY()); |
104 | 0 | if(vertex != null) { // get ready to make an edge |
105 | 0 | startVertex = vertex; |
106 | 0 | down = e.getPoint(); |
107 | 0 | transformEdgeShape(down, down); |
108 | 0 | vv.addPostRenderPaintable(edgePaintable); |
109 | 0 | if((e.getModifiers() & MouseEvent.SHIFT_MASK) != 0) { |
110 | 0 | edgeIsDirected = true; |
111 | 0 | transformArrowShape(down, e.getPoint()); |
112 | 0 | vv.addPostRenderPaintable(arrowPaintable); |
113 | } | |
114 | } else { // make a new vertex | |
115 | 0 | Graph graph = vv.getGraphLayout().getGraph(); |
116 | 0 | Vertex newVertex = new SparseVertex(); |
117 | 0 | vertexLocations.setLocation(newVertex, vv.inverseTransform(e.getPoint())); |
118 | 0 | Layout layout = vv.getGraphLayout(); |
119 | 0 | for(Iterator iterator=graph.getVertices().iterator(); iterator.hasNext(); ) { |
120 | 0 | layout.lockVertex((Vertex)iterator.next()); |
121 | } | |
122 | 0 | graph.addVertex(newVertex); |
123 | 0 | vv.getModel().restart(); |
124 | 0 | for(Iterator iterator=graph.getVertices().iterator(); iterator.hasNext(); ) { |
125 | 0 | layout.unlockVertex((Vertex)iterator.next()); |
126 | } | |
127 | 0 | vv.repaint(); |
128 | } | |
129 | } | |
130 | } | |
131 | 0 | } |
132 | ||
133 | /** | |
134 | * If startVertex is non-null, and the mouse is released over an | |
135 | * existing vertex, create an undirected edge from startVertex to | |
136 | * the vertex under the mouse pointer. If shift was also pressed, | |
137 | * create a directed edge instead. | |
138 | */ | |
139 | public void mouseReleased(MouseEvent e) { | |
140 | 0 | if(checkModifiers(e)) { |
141 | 0 | final VisualizationViewer vv = |
142 | (VisualizationViewer)e.getSource(); | |
143 | 0 | final Point2D p = vv.inverseViewTransform(e.getPoint()); |
144 | 0 | PickSupport pickSupport = vv.getPickSupport(); |
145 | 0 | if(pickSupport != null) { |
146 | 0 | final Vertex vertex = pickSupport.getVertex(p.getX(), p.getY()); |
147 | 0 | if(vertex != null && startVertex != null) { |
148 | 0 | Graph graph = vv.getGraphLayout().getGraph(); |
149 | 0 | if(edgeIsDirected) { |
150 | 0 | graph.addEdge(new DirectedSparseEdge(startVertex, vertex)); |
151 | } else { | |
152 | 0 | graph.addEdge(new UndirectedSparseEdge(startVertex, vertex)); |
153 | } | |
154 | 0 | vv.repaint(); |
155 | } | |
156 | } | |
157 | 0 | startVertex = null; |
158 | 0 | down = null; |
159 | 0 | edgeIsDirected = false; |
160 | 0 | vv.removePostRenderPaintable(edgePaintable); |
161 | 0 | vv.removePostRenderPaintable(arrowPaintable); |
162 | } | |
163 | 0 | } |
164 | ||
165 | /** | |
166 | * If startVertex is non-null, stretch an edge shape between | |
167 | * startVertex and the mouse pointer to simulate edge creation | |
168 | */ | |
169 | public void mouseDragged(MouseEvent e) { | |
170 | 0 | if(checkModifiers(e)) { |
171 | 0 | if(startVertex != null) { |
172 | 0 | transformEdgeShape(down, e.getPoint()); |
173 | 0 | if(edgeIsDirected) { |
174 | 0 | transformArrowShape(down, e.getPoint()); |
175 | } | |
176 | } | |
177 | } | |
178 | 0 | } |
179 | ||
180 | /** | |
181 | * code lifted from PluggableRenderer to move an edge shape into an | |
182 | * arbitrary position | |
183 | */ | |
184 | private void transformEdgeShape(Point2D down, Point2D out) { | |
185 | 0 | float x1 = (float) down.getX(); |
186 | 0 | float y1 = (float) down.getY(); |
187 | 0 | float x2 = (float) out.getX(); |
188 | 0 | float y2 = (float) out.getY(); |
189 | ||
190 | 0 | AffineTransform xform = AffineTransform.getTranslateInstance(x1, y1); |
191 | ||
192 | 0 | float dx = x2-x1; |
193 | 0 | float dy = y2-y1; |
194 | 0 | float thetaRadians = (float) Math.atan2(dy, dx); |
195 | 0 | xform.rotate(thetaRadians); |
196 | 0 | float dist = (float) Math.sqrt(dx*dx + dy*dy); |
197 | 0 | xform.scale(dist / rawEdge.getBounds().getWidth(), 1.0); |
198 | 0 | edgeShape = xform.createTransformedShape(rawEdge); |
199 | 0 | } |
200 | ||
201 | private void transformArrowShape(Point2D down, Point2D out) { | |
202 | 0 | float x1 = (float) down.getX(); |
203 | 0 | float y1 = (float) down.getY(); |
204 | 0 | float x2 = (float) out.getX(); |
205 | 0 | float y2 = (float) out.getY(); |
206 | ||
207 | 0 | AffineTransform xform = AffineTransform.getTranslateInstance(x2, y2); |
208 | ||
209 | 0 | float dx = x2-x1; |
210 | 0 | float dy = y2-y1; |
211 | 0 | float thetaRadians = (float) Math.atan2(dy, dx); |
212 | 0 | xform.rotate(thetaRadians); |
213 | 0 | arrowShape = xform.createTransformedShape(rawArrowShape); |
214 | 0 | } |
215 | ||
216 | /** | |
217 | * Used for the edge creation visual effect during mouse drag | |
218 | */ | |
219 | class EdgePaintable implements Paintable { | |
220 | ||
221 | public void paint(Graphics g) { | |
222 | if(edgeShape != null) { | |
223 | Color oldColor = g.getColor(); | |
224 | g.setColor(Color.black); | |
225 | ((Graphics2D)g).draw(edgeShape); | |
226 | g.setColor(oldColor); | |
227 | } | |
228 | } | |
229 | ||
230 | public boolean useTransform() { | |
231 | return false; | |
232 | } | |
233 | } | |
234 | ||
235 | /** | |
236 | * Used for the directed edge creation visual effect during mouse drag | |
237 | */ | |
238 | class ArrowPaintable implements Paintable { | |
239 | ||
240 | public void paint(Graphics g) { | |
241 | if(arrowShape != null) { | |
242 | Color oldColor = g.getColor(); | |
243 | g.setColor(Color.black); | |
244 | ((Graphics2D)g).fill(arrowShape); | |
245 | g.setColor(oldColor); | |
246 | } | |
247 | } | |
248 | ||
249 | public boolean useTransform() { | |
250 | return false; | |
251 | } | |
252 | } | |
253 | 0 | public void mouseClicked(MouseEvent e) {} |
254 | 0 | public void mouseEntered(MouseEvent e) {} |
255 | 0 | public void mouseExited(MouseEvent e) {} |
256 | 0 | public void mouseMoved(MouseEvent e) {} |
257 | } |
this report was generated by version 1.0.5 of jcoverage. |
copyright © 2003, jcoverage ltd. all rights reserved. |