Line | Hits | Source |
---|---|---|
1 | /* | |
2 | * Copyright (c) 2005, the JUNG Project and the Regents of the University | |
3 | * of California | |
4 | * All rights reserved. | |
5 | * | |
6 | * This software is open-source under the BSD license; see either | |
7 | * "license.txt" or | |
8 | * http://jung.sourceforge.net/license.txt for a description. | |
9 | * Created on Mar 8, 2005 | |
10 | * | |
11 | */ | |
12 | package edu.uci.ics.jung.visualization.control; | |
13 | ||
14 | import java.awt.Color; | |
15 | import java.awt.Cursor; | |
16 | import java.awt.Graphics; | |
17 | import java.awt.Graphics2D; | |
18 | import java.awt.Point; | |
19 | import java.awt.event.InputEvent; | |
20 | import java.awt.event.MouseEvent; | |
21 | import java.awt.event.MouseListener; | |
22 | import java.awt.event.MouseMotionListener; | |
23 | import java.awt.geom.Point2D; | |
24 | import java.awt.geom.Rectangle2D; | |
25 | import java.util.ConcurrentModificationException; | |
26 | import java.util.Iterator; | |
27 | ||
28 | import javax.swing.JComponent; | |
29 | ||
30 | import edu.uci.ics.jung.graph.Edge; | |
31 | import edu.uci.ics.jung.graph.Vertex; | |
32 | import edu.uci.ics.jung.visualization.Layout; | |
33 | import edu.uci.ics.jung.visualization.PickSupport; | |
34 | import edu.uci.ics.jung.visualization.PickedState; | |
35 | import edu.uci.ics.jung.visualization.VisualizationViewer; | |
36 | import edu.uci.ics.jung.visualization.VisualizationViewer.Paintable; | |
37 | ||
38 | /** | |
39 | * PickingGraphMousePlugin supports the picking of graph elements | |
40 | * with the mouse. MouseButtonOne picks a single vertex | |
41 | * or edge, and MouseButtonTwo adds to the set of selected Vertices | |
42 | * or Edges. If a Vertex is selected and the mouse is dragged while | |
43 | * on the selected Vertex, then that Vertex will be repositioned to | |
44 | * follow the mouse until the button is released. | |
45 | * | |
46 | * @author Tom Nelson | |
47 | */ | |
48 | public class PickingGraphMousePlugin extends AbstractGraphMousePlugin | |
49 | implements MouseListener, MouseMotionListener { | |
50 | ||
51 | /** | |
52 | * the picked Vertex, if any | |
53 | */ | |
54 | protected Vertex vertex; | |
55 | ||
56 | /** | |
57 | * the picked Edge, if any | |
58 | */ | |
59 | protected Edge edge; | |
60 | ||
61 | /** | |
62 | * the x distance from the picked vertex center to the mouse point | |
63 | */ | |
64 | protected double offsetx; | |
65 | ||
66 | /** | |
67 | * the y distance from the picked vertex center to the mouse point | |
68 | */ | |
69 | protected double offsety; | |
70 | ||
71 | /** | |
72 | * controls whether the Vertices may be moved with the mouse | |
73 | */ | |
74 | protected boolean locked; | |
75 | ||
76 | /** | |
77 | * additional modifiers for the action of adding to an existing | |
78 | * selection | |
79 | */ | |
80 | protected int addToSelectionModifiers; | |
81 | ||
82 | /** | |
83 | * used to draw a rectangle to contain picked vertices | |
84 | */ | |
85 | 0 | protected Rectangle2D rect = new Rectangle2D.Float(); |
86 | ||
87 | /** | |
88 | * the Paintable for the lens picking rectangle | |
89 | */ | |
90 | protected Paintable lensPaintable; | |
91 | ||
92 | /** | |
93 | * color for the picking rectangle | |
94 | */ | |
95 | 0 | protected Color lensColor = Color.cyan; |
96 | ||
97 | /** | |
98 | * create an instance with default settings | |
99 | */ | |
100 | public PickingGraphMousePlugin() { | |
101 | 0 | this(InputEvent.BUTTON1_MASK, InputEvent.BUTTON1_MASK | InputEvent.SHIFT_MASK); |
102 | 0 | } |
103 | ||
104 | /** | |
105 | * create an instance with overides | |
106 | * @param selectionModifiers for primary selection | |
107 | * @param addToSelectionModifiers for additional selection | |
108 | */ | |
109 | public PickingGraphMousePlugin(int selectionModifiers, int addToSelectionModifiers) { | |
110 | 0 | super(selectionModifiers); |
111 | 0 | this.addToSelectionModifiers = addToSelectionModifiers; |
112 | 0 | this.lensPaintable = new LensPaintable(); |
113 | 0 | this.cursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR); |
114 | 0 | } |
115 | ||
116 | /** | |
117 | * @return Returns the lensColor. | |
118 | */ | |
119 | public Color getLensColor() { | |
120 | 0 | return lensColor; |
121 | } | |
122 | ||
123 | /** | |
124 | * @param lensColor The lensColor to set. | |
125 | */ | |
126 | public void setLensColor(Color lensColor) { | |
127 | 0 | this.lensColor = lensColor; |
128 | 0 | } |
129 | ||
130 | /** | |
131 | * a Paintable to draw the rectangle used to pick multiple | |
132 | * Vertices | |
133 | * @author Tom Nelson - RABA Technologies | |
134 | * | |
135 | */ | |
136 | class LensPaintable implements Paintable { | |
137 | ||
138 | public void paint(Graphics g) { | |
139 | Color oldColor = g.getColor(); | |
140 | g.setColor(lensColor); | |
141 | ((Graphics2D)g).draw(rect); | |
142 | g.setColor(oldColor); | |
143 | } | |
144 | ||
145 | public boolean useTransform() { | |
146 | return false; | |
147 | } | |
148 | } | |
149 | ||
150 | /** | |
151 | * For primary modifiers (default, MouseButton1): | |
152 | * pick a single Vertex or Edge that | |
153 | * is under the mouse pointer. If no Vertex or edge is under | |
154 | * the pointer, unselect all picked Vertices and edges, and | |
155 | * set up to draw a rectangle for multiple selection | |
156 | * of contained Vertices. | |
157 | * For additional selection (default Shift+MouseButton1): | |
158 | * Add to the selection, a single Vertex or Edge that is | |
159 | * under the mouse pointer. If a previously picked Vertex | |
160 | * or Edge is under the pointer, it is un-picked. | |
161 | * If no vertex or Edge is under the pointer, set up | |
162 | * to draw a multiple selection rectangle (as above) | |
163 | * but do not unpick previously picked elements. | |
164 | * | |
165 | * @param e the event | |
166 | */ | |
167 | public void mousePressed(MouseEvent e) { | |
168 | 0 | down = e.getPoint(); |
169 | 0 | VisualizationViewer vv = (VisualizationViewer)e.getSource(); |
170 | 0 | PickSupport pickSupport = vv.getPickSupport(); |
171 | 0 | PickedState pickedState = vv.getPickedState(); |
172 | 0 | if(pickSupport != null && pickedState != null) { |
173 | 0 | Layout layout = vv.getGraphLayout(); |
174 | 0 | if(e.getModifiers() == modifiers) { |
175 | 0 | vv.addPostRenderPaintable(lensPaintable); |
176 | 0 | rect.setFrameFromDiagonal(down,down); |
177 | // p is the screen point for the mouse event | |
178 | 0 | Point2D p = e.getPoint(); |
179 | // take away the view transform | |
180 | 0 | Point2D ip = vv.inverseViewTransform(p); |
181 | ||
182 | 0 | vertex = pickSupport.getVertex(ip.getX(), ip.getY()); |
183 | 0 | if(vertex != null) { |
184 | 0 | if(pickedState.isPicked(vertex) == false) { |
185 | 0 | pickedState.clearPickedVertices(); |
186 | 0 | pickedState.pick(vertex, true); |
187 | } | |
188 | // layout.getLocation applies the layout transformer so | |
189 | // q is transformed by the layout transformer only | |
190 | 0 | Point2D q = layout.getLocation(vertex); |
191 | // transform the mouse point to graph coordinate system | |
192 | 0 | Point2D gp = vv.inverseLayoutTransform(ip); |
193 | ||
194 | 0 | offsetx = (float) (gp.getX()-q.getX()); |
195 | 0 | offsety = (float) (gp.getY()-q.getY()); |
196 | 0 | } else if((edge = pickSupport.getEdge(ip.getX(), ip.getY())) != null) { |
197 | 0 | pickedState.clearPickedEdges(); |
198 | 0 | pickedState.pick(edge, true); |
199 | } else { | |
200 | 0 | pickedState.clearPickedEdges(); |
201 | 0 | pickedState.clearPickedVertices(); |
202 | } | |
203 | ||
204 | 0 | } else if(e.getModifiers() == addToSelectionModifiers) { |
205 | 0 | vv.addPostRenderPaintable(lensPaintable); |
206 | 0 | rect.setFrameFromDiagonal(down,down); |
207 | 0 | Point2D p = e.getPoint(); |
208 | // remove view transform | |
209 | 0 | Point2D ip = vv.inverseViewTransform(p); |
210 | 0 | vertex = pickSupport.getVertex(ip.getX(), ip.getY()); |
211 | 0 | if(vertex != null) { |
212 | 0 | boolean wasThere = pickedState.pick(vertex, !pickedState.isPicked(vertex)); |
213 | 0 | if(wasThere) { |
214 | 0 | vertex = null; |
215 | } else { | |
216 | ||
217 | // layout.getLocation applies the layout transformer so | |
218 | // q is transformed by the layout transformer only | |
219 | 0 | Point2D q = layout.getLocation(vertex); |
220 | // translate mouse point to graph coord system | |
221 | 0 | Point2D gp = vv.inverseLayoutTransform(ip); |
222 | ||
223 | 0 | offsetx = (float) (gp.getX()-q.getX()); |
224 | 0 | offsety = (float) (gp.getY()-q.getY()); |
225 | } | |
226 | 0 | } else if((edge = pickSupport.getEdge(ip.getX(), ip.getY())) != null) { |
227 | 0 | pickedState.pick(edge, !pickedState.isPicked(edge)); |
228 | } | |
229 | } | |
230 | } | |
231 | 0 | if(vertex != null) e.consume(); |
232 | 0 | } |
233 | ||
234 | /** | |
235 | * If the mouse is dragging a rectangle, pick the | |
236 | * Vertices contained in that rectangle | |
237 | * | |
238 | * clean up settings from mousePressed | |
239 | */ | |
240 | public void mouseReleased(MouseEvent e) { | |
241 | 0 | VisualizationViewer vv = (VisualizationViewer)e.getSource(); |
242 | 0 | if(e.getModifiers() == modifiers) { |
243 | 0 | if(down != null) { |
244 | 0 | Point2D out = e.getPoint(); |
245 | 0 | if(vertex == null && heyThatsTooClose(down, out, 5) == false) { |
246 | 0 | pickContainedVertices(vv, true); |
247 | } | |
248 | } | |
249 | 0 | } else if(e.getModifiers() == this.addToSelectionModifiers) { |
250 | 0 | if(down != null) { |
251 | 0 | Point2D out = e.getPoint(); |
252 | 0 | if(vertex == null && heyThatsTooClose(down,out,5) == false) { |
253 | 0 | pickContainedVertices(vv, false); |
254 | } | |
255 | } | |
256 | } | |
257 | 0 | down = null; |
258 | 0 | vertex = null; |
259 | 0 | edge = null; |
260 | 0 | rect.setFrame(0,0,0,0); |
261 | 0 | vv.removePostRenderPaintable(lensPaintable); |
262 | 0 | } |
263 | ||
264 | /** | |
265 | * If the mouse is over a picked vertex, drag all picked | |
266 | * vertices with the mouse. | |
267 | * If the mouse is not over a Vertex, draw the rectangle | |
268 | * to select multiple Vertices | |
269 | * | |
270 | */ | |
271 | public void mouseDragged(MouseEvent e) { | |
272 | 0 | if(locked == false) { |
273 | 0 | VisualizationViewer vv = (VisualizationViewer)e.getSource(); |
274 | 0 | if(vertex != null) { |
275 | 0 | Point p = e.getPoint(); |
276 | 0 | Point2D graphPoint = vv.inverseTransform(p); |
277 | 0 | Point2D graphDown = vv.inverseTransform(down); |
278 | 0 | Layout layout = vv.getGraphLayout(); |
279 | 0 | double dx = graphPoint.getX()-graphDown.getX(); |
280 | 0 | double dy = graphPoint.getY()-graphDown.getY(); |
281 | 0 | PickedState ps = vv.getPickedState(); |
282 | ||
283 | 0 | for(Iterator iterator=ps.getPickedVertices().iterator(); iterator.hasNext(); ) { |
284 | 0 | Vertex v = (Vertex)iterator.next(); |
285 | 0 | Point2D vp = layout.getLocation(v); |
286 | 0 | layout.forceMove(v, vp.getX()+dx, vp.getY()+dy); |
287 | } | |
288 | 0 | down = p; |
289 | ||
290 | } else { | |
291 | 0 | Point2D out = e.getPoint(); |
292 | 0 | if(e.getModifiers() == this.addToSelectionModifiers || |
293 | e.getModifiers() == modifiers) { | |
294 | 0 | rect.setFrameFromDiagonal(down,out); |
295 | } | |
296 | } | |
297 | 0 | if(vertex != null) e.consume(); |
298 | } | |
299 | 0 | } |
300 | ||
301 | /** | |
302 | * rejects picking if the rectangle is too small, like | |
303 | * if the user meant to select one vertex but moved the | |
304 | * mouse slightly | |
305 | * @param p | |
306 | * @param q | |
307 | * @param min | |
308 | * @return | |
309 | */ | |
310 | private boolean heyThatsTooClose(Point2D p, Point2D q, double min) { | |
311 | 0 | return Math.abs(p.getX()-q.getX()) < min && |
312 | Math.abs(p.getY()-q.getY()) < min; | |
313 | } | |
314 | ||
315 | /** | |
316 | * pick the vertices inside the rectangle | |
317 | * | |
318 | */ | |
319 | protected void pickContainedVertices(VisualizationViewer vv, boolean clear) { | |
320 | ||
321 | 0 | Layout layout = vv.getGraphLayout(); |
322 | 0 | PickedState pickedState = vv.getPickedState(); |
323 | 0 | if(pickedState != null) { |
324 | 0 | if(clear) { |
325 | 0 | pickedState.clearPickedVertices(); |
326 | } | |
327 | while(true) { | |
328 | try { | |
329 | 0 | for (Iterator iter=layout.getGraph().getVertices().iterator(); iter.hasNext(); ) { |
330 | 0 | Vertex v = (Vertex) iter.next(); |
331 | 0 | if(rect.contains(vv.transform(layout.getLocation(v)))) { |
332 | 0 | pickedState.pick(v, true); |
333 | } | |
334 | } | |
335 | 0 | break; |
336 | 0 | } catch(ConcurrentModificationException cme) {} |
337 | } | |
338 | } | |
339 | 0 | } |
340 | ||
341 | public void mouseClicked(MouseEvent e) { | |
342 | 0 | } |
343 | ||
344 | public void mouseEntered(MouseEvent e) { | |
345 | 0 | JComponent c = (JComponent)e.getSource(); |
346 | 0 | c.setCursor(cursor); |
347 | 0 | } |
348 | ||
349 | public void mouseExited(MouseEvent e) { | |
350 | 0 | JComponent c = (JComponent)e.getSource(); |
351 | 0 | c.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); |
352 | 0 | } |
353 | ||
354 | public void mouseMoved(MouseEvent e) { | |
355 | 0 | } |
356 | ||
357 | /** | |
358 | * @return Returns the locked. | |
359 | */ | |
360 | public boolean isLocked() { | |
361 | 0 | return locked; |
362 | } | |
363 | ||
364 | /** | |
365 | * @param locked The locked to set. | |
366 | */ | |
367 | public void setLocked(boolean locked) { | |
368 | 0 | this.locked = locked; |
369 | 0 | } |
370 | } |
this report was generated by version 1.0.5 of jcoverage. |
copyright © 2003, jcoverage ltd. all rights reserved. |