View Javadoc

1   /*******************************************************************************
2    * Copyright 2005-2006, CHISEL Group, University of Victoria, Victoria, BC, Canada.
3    * All rights reserved. This program and the accompanying materials
4    * are made available under the terms of the Eclipse Public License v1.0
5    * which accompanies this distribution, and is available at
6    * http://www.eclipse.org/legal/epl-v10.html
7    *
8    * Contributors:
9    *     The Chisel Group, University of Victoria
10   *******************************************************************************/
11  package pl.edu.agh.cast.tool;
12  
13  import java.beans.PropertyChangeEvent;
14  import java.beans.PropertyChangeListener;
15  
16  import org.eclipse.draw2d.ColorConstants;
17  import org.eclipse.draw2d.Cursors;
18  import org.eclipse.draw2d.FigureListener;
19  import org.eclipse.draw2d.IFigure;
20  import org.eclipse.draw2d.RectangleFigure;
21  import org.eclipse.draw2d.Viewport;
22  import org.eclipse.draw2d.geometry.Dimension;
23  import org.eclipse.draw2d.geometry.Point;
24  import org.eclipse.draw2d.geometry.Rectangle;
25  import org.eclipse.gef.DragTracker;
26  import org.eclipse.gef.EditPartViewer;
27  import org.eclipse.gef.GraphicalEditPart;
28  import org.eclipse.gef.RequestConstants;
29  import org.eclipse.gef.editparts.ZoomListener;
30  import org.eclipse.gef.editparts.ZoomManager;
31  import org.eclipse.gef.tools.AbstractTool;
32  import org.eclipse.swt.events.ControlEvent;
33  import org.eclipse.swt.events.ControlListener;
34  import org.eclipse.swt.events.MouseEvent;
35  
36  /**
37   * A tool that is used in the graph overview viewer that allows the user to select an area of the graph to zoom on.
38   *
39   * @author Del Myers
40   *
41   */
42  public class OverviewRectangleTool extends AbstractTool implements DragTracker, ControlListener, ZoomListener,
43          PropertyChangeListener, FigureListener {
44  
45  	public static final String ZOOM_MANAGER = "zoomManager"; //$NON-NLS-1$
46  
47  	private RectangleFigure focusFigure;
48  
49  	private int dragState;
50  
51  	private Rectangle startBounds;
52  
53  	private TargetShape target;
54  
55  	private FigureListener focusFigureListener;
56  
57  	private Rectangle referenceBounds;
58  
59  	private ZoomManager currentManager;
60  
61  	private static final int OUTSIDE = 0;
62  
63  	private static final int INSIDE = 1 << 1;
64  
65  	private static final int NORTH = INSIDE | 1 << 2;
66  
67  	private static final int EAST = INSIDE | 1 << 3;
68  
69  	private static final int SOUTH = INSIDE | 1 << 4;
70  
71  	private static final int WEST = INSIDE | 1 << 5;
72  
73  	private static final int NORTHWEST = INSIDE | NORTH | WEST;
74  
75  	private static final int NORTHEAST = INSIDE | NORTH | EAST;
76  
77  	private static final int SOUTHWEST = INSIDE | SOUTH | WEST;
78  
79  	private static final int SOUTHEAST = INSIDE | SOUTH | EAST;
80  
81  	private class FocusMoveListener implements FigureListener {
82  
83  		/*
84  		 * (non-Javadoc)
85  		 *
86  		 * @see org.eclipse.draw2d.FigureListener#figureMoved(org.eclipse.draw2d.IFigure)
87  		 */
88  		public void figureMoved(IFigure source) {
89  			// move the target to the center of the figure.
90  			setTargetCenter(source.getBounds().getCenter());
91  		}
92  
93  	}
94  
95  	/**
96  	 *
97  	 */
98  	public OverviewRectangleTool() {
99  		setUnloadWhenFinished(false);
100 		referenceBounds = new Rectangle();
101 		// setEditDomain(new EditDomain());
102 	}
103 
104 	/*
105 	 * (non-Javadoc)
106 	 *
107 	 * @see org.eclipse.gef.tools.AbstractTool#handleDragStarted()
108 	 */
109 	protected boolean handleDragStarted() {
110 		setState(STATE_DRAG);
111 		setDragState(getRelativeCursorLocation(getStartLocation()));
112 		Rectangle bounds = getFeedBackFigure().getBounds().getCopy();
113 		getFeedBackFigure().translateToAbsolute(bounds);
114 		setDragStartBounds(bounds);
115 		return true;
116 	}
117 
118 	/**
119 	 * @param bounds
120 	 */
121 	private void setDragStartBounds(Rectangle bounds) {
122 		this.startBounds = bounds.getCopy();
123 
124 	}
125 
126 	private Rectangle getDragStartBounds() {
127 		return startBounds;
128 	}
129 
130 	private void setDragState(int state) {
131 		this.dragState = state;
132 	}
133 
134 	/**
135 	 * @return the dragState
136 	 */
137 	private int getDragState() {
138 		return dragState;
139 	}
140 
141 	/*
142 	 * (non-Javadoc)
143 	 *
144 	 * @see org.eclipse.gef.tools.AbstractTool#handleDragInProgress()
145 	 */
146 	protected boolean handleDragInProgress() {
147 		setState(STATE_DRAG_IN_PROGRESS);
148 		Dimension delta = getDragMoveDelta();
149 		Point start = getStartLocation();
150 		RectangleFigure feedBack = getFeedBackFigure();
151 		boolean moving = false;
152 		int state = getDragState();
153 		Rectangle oldBounds = getDragStartBounds();
154 		Rectangle newBounds = oldBounds.getCopy();
155 		if (isNorth(state)) {
156 			newBounds.y = oldBounds.y + delta.height;
157 			newBounds.height = oldBounds.height - delta.height;
158 		} else if (isSouth(state)) {
159 			newBounds.height = oldBounds.height + delta.height;
160 		}
161 		if (isWest(state)) {
162 			newBounds.x = oldBounds.x + delta.width;
163 			newBounds.width = oldBounds.width - delta.width;
164 		} else if (isEast(state)) {
165 			newBounds.width = oldBounds.width + delta.width;
166 		}
167 		if (!(isWest(state) || isEast(state) || isNorth(state) || isSouth(state))) {
168 			if (isInside(state)) {
169 				moving = true;
170 				newBounds.x = oldBounds.x + delta.width;
171 				newBounds.y = oldBounds.y + delta.height;
172 			} else {
173 				// a new rectangle is drawn.
174 				newBounds = new Rectangle(start, delta);
175 			}
176 		}
177 		// adjust the bounds for negative widths and heights
178 		if (newBounds.width < 0) {
179 			newBounds.x += newBounds.width;
180 			newBounds.width = -newBounds.width;
181 		}
182 		if (newBounds.height < 0) {
183 			newBounds.y += newBounds.height;
184 			newBounds.height = -newBounds.height;
185 		}
186 		int minX = getCurrentViewer().getControl().getBounds().x;
187 		int minY = getCurrentViewer().getControl().getBounds().y;
188 		int maxWidth = getCurrentViewer().getControl().getBounds().width;
189 		int maxHeight = getCurrentViewer().getControl().getBounds().height;
190 		if (newBounds.x < minX || newBounds.y < minY || newBounds.x + newBounds.width > minX + maxWidth
191 		        || newBounds.y + newBounds.height > minY + maxHeight) {
192 			if (moving) {
193 				if (newBounds.x < minX) {
194 					newBounds.x = minX;
195 				}
196 				if (newBounds.y < minY) {
197 					newBounds.y = minY;
198 				}
199 				if (newBounds.x + newBounds.width > minX + maxWidth) {
200 					newBounds.x = maxWidth - newBounds.width;
201 				}
202 				if (newBounds.y + newBounds.height > minY + maxHeight) {
203 					newBounds.y = maxHeight - newBounds.height;
204 				}
205 			} else {
206 				if (newBounds.x < minX) {
207 					newBounds.width = newBounds.x + newBounds.width - minX;
208 					newBounds.x = minX;
209 				}
210 				if (newBounds.y < minY) {
211 					newBounds.height = newBounds.y + newBounds.height - minY;
212 					newBounds.y = minY;
213 				}
214 				if (newBounds.x + newBounds.width > minX + maxWidth) {
215 					newBounds.width = maxWidth - newBounds.x;
216 				}
217 				if (newBounds.y + newBounds.height > minY + maxHeight) {
218 					newBounds.height = maxHeight - newBounds.y;
219 				}
220 			}
221 		}
222 		feedBack.translateToRelative(newBounds);
223 		feedBack.setBounds(newBounds);
224 		return true;
225 	}
226 
227 	/*
228 	 * (non-Javadoc)
229 	 *
230 	 * @see org.eclipse.gef.tools.AbstractTool#handleMove()
231 	 */
232 	protected boolean handleMove() {
233 		Point location = getLocation();
234 		switch (getRelativeCursorLocation(location)) {
235 			case NORTH:
236 			case SOUTH:
237 				setCursor(Cursors.SIZENS);
238 				break;
239 			case EAST:
240 			case WEST:
241 				setCursor(Cursors.SIZEWE);
242 				break;
243 			case NORTHWEST:
244 			case SOUTHEAST:
245 				setCursor(Cursors.SIZENWSE);
246 				break;
247 			case NORTHEAST:
248 			case SOUTHWEST:
249 				setCursor(Cursors.SIZENESW);
250 				break;
251 			case INSIDE:
252 				setCursor(Cursors.SIZEALL);
253 				break;
254 			default:
255 				setCursor(getDefaultCursor());
256 
257 		}
258 		return true;
259 	}
260 
261 	/*
262 	 * (non-Javadoc)
263 	 *
264 	 * @see org.eclipse.gef.tools.AbstractTool#handleButtonUp(int)
265 	 */
266 	protected boolean handleButtonUp(int button) {
267 		handleFinished();
268 		return true;
269 	}
270 
271 	/*
272 	 * (non-Javadoc)
273 	 *
274 	 * @see org.eclipse.gef.tools.AbstractTool#handleFinished()
275 	 */
276 	protected void handleFinished() {
277 		if (isInState(STATE_DRAG_IN_PROGRESS) || isInState(STATE_DRAG)) {
278 
279 		} else {
280 			Point location = getLocation();
281 			getFeedBackFigure().setBounds(new Rectangle(location.x, location.y, 0, 0));
282 		}
283 		setState(STATE_TERMINAL);
284 		resetFlags();
285 		zoomTo(getFeedBackFigure().getBounds().getCopy());
286 
287 		// super.handleFinished();
288 	}
289 
290 	/**
291 	 * @param copy
292 	 */
293 	private void zoomTo(Rectangle rect) {
294 		referenceBounds = rect.getCopy();
295 		GraphicalEditPart part = (GraphicalEditPart)getCurrentViewer().getContents();
296 		if (part != null) {
297 			part.getFigure().translateFromParent(referenceBounds);
298 			if (rect.isEmpty()) {
299 				// make sure that it didn't scale to a size greater than 0.
300 				referenceBounds.setSize(0, 0);
301 			}
302 			if (this.currentManager == null) {
303 
304 			}
305 
306 			if (this.currentManager != null) {
307 
308 				this.currentManager.zoomTo(referenceBounds);
309 			}
310 			if (referenceBounds.isEmpty()) {
311 				// reset the figure so as to not jar the user.
312 				getFeedBackFigure().setBounds(rect);
313 			}
314 		}
315 	}
316 
317 	boolean isNorth(int loc) {
318 		loc = (isInside(loc)) ? loc ^ INSIDE : loc;
319 		return (loc & NORTH) != 0;
320 	}
321 
322 	boolean isEast(int loc) {
323 		loc = (isInside(loc)) ? loc ^ INSIDE : loc;
324 		return (loc & EAST) != 0;
325 	}
326 
327 	boolean isSouth(int loc) {
328 		loc = (isInside(loc)) ? loc ^ INSIDE : loc;
329 		return (loc & SOUTH) != 0;
330 	}
331 
332 	boolean isWest(int loc) {
333 		loc = (isInside(loc)) ? loc ^ INSIDE : loc;
334 		return (loc & WEST) != 0;
335 	}
336 
337 	boolean isInside(int loc) {
338 		return (loc & INSIDE) != 0;
339 	}
340 
341 	private int getRelativeCursorLocation(Point location) {
342 		RectangleFigure feedBack = getFeedBackFigure();
343 		Rectangle bounds = feedBack.getBounds();
344 		location = location.getCopy();
345 		feedBack.translateToRelative(location);
346 		int result = OUTSIDE;
347 		if (feedBack.containsPoint(location)) {
348 			result |= INSIDE;
349 			// check to see the bounds to find out which
350 			// cursor to use
351 			if (Math.abs(location.y - bounds.y) <= 2) {
352 				result |= NORTH;
353 			} else if (Math.abs(location.y - (bounds.y + bounds.height - 1)) <= 2) {
354 				result |= SOUTH;
355 			}
356 			if (Math.abs(location.x - bounds.x) <= 2) {
357 				result |= WEST;
358 			} else if (Math.abs(location.x - (bounds.x + bounds.width - 1)) <= 2) {
359 				result |= EAST;
360 			}
361 		}
362 		return result;
363 	}
364 
365 	private RectangleFigure getFeedBackFigure() {
366 		if (focusFigure == null) {
367 			focusFigure = new RectangleFigure();
368 			focusFigure.setForegroundColor(ColorConstants.red);
369 			focusFigure.setFill(false);
370 			focusFigure.setLineWidth(2);
371 			focusFigureListener = new FocusMoveListener();
372 			focusFigure.addFigureListener(focusFigureListener);
373 			focusFigure.setBounds(new Rectangle());
374 			addFeedback(focusFigure);
375 			// referenceBounds = focusFigure.getParent().getBounds().getCopy();
376 			// focusFigure.getParent().addFigureListener(this);
377 		}
378 		return focusFigure;
379 	}
380 
381 	private TargetShape getTargetFigure() {
382 		if (target == null) {
383 			target = new TargetShape();
384 			target.setForegroundColor(ColorConstants.red);
385 			target.setFill(false);
386 			target.setBounds(new Rectangle(-10, -10, 20, 20));
387 			target.setLineWidth(2);
388 			addFeedback(target);
389 		}
390 		return target;
391 	}
392 
393 	private void setTargetCenter(Point center) {
394 		Point newCenter = center.getCopy();
395 		newCenter.translate(-getTargetFigure().getSize().width / 2, -getTargetFigure().getSize().height / 2);
396 		getTargetFigure().setLocation(newCenter);
397 	}
398 
399 	/*
400 	 * (non-Javadoc)
401 	 *
402 	 * @see org.eclipse.gef.tools.AbstractTool#deactivate()
403 	 */
404 	public void deactivate() {
405 		if (focusFigure != null) {
406 			focusFigure.removeFigureListener(focusFigureListener);
407 			removeFeedback(focusFigure);
408 			focusFigure = null;
409 		}
410 		if (target != null) {
411 			removeFeedback(target);
412 			target = null;
413 		}
414 		if (getCurrentViewer() != null) {
415 			getCurrentViewer().getControl().removeControlListener(this);
416 			if (getCurrentViewer().getContents() instanceof GraphicalEditPart) {
417 				((GraphicalEditPart)getCurrentViewer().getContents()).getFigure().removeFigureListener(this);
418 			}
419 		}
420 		if (this.currentManager != null) {
421 			this.currentManager.removeZoomListener(this);
422 			this.currentManager.getViewport().getHorizontalRangeModel().removePropertyChangeListener(this);
423 			this.currentManager.getViewport().getVerticalRangeModel().removePropertyChangeListener(this);
424 		}
425 		super.deactivate();
426 	}
427 
428 	/*
429 	 * (non-Javadoc)
430 	 *
431 	 * @see org.eclipse.gef.tools.AbstractTool#activate()
432 	 */
433 	public void activate() {
434 
435 		super.activate();
436 	}
437 
438 	/*
439 	 * (non-Javadoc)
440 	 *
441 	 * @see org.eclipse.gef.tools.AbstractTool#getCommandName()
442 	 */
443 	protected String getCommandName() {
444 		return RequestConstants.REQ_SELECTION;
445 	}
446 
447 	/*
448 	 * (non-Javadoc)
449 	 *
450 	 * @see org.eclipse.gef.tools.AbstractTool#setViewer(org.eclipse.gef.EditPartViewer)
451 	 */
452 	public void setViewer(EditPartViewer viewer) {
453 		if (getCurrentViewer() != null) {
454 			getCurrentViewer().getControl().removeControlListener(this);
455 			GraphicalEditPart part = (GraphicalEditPart)getCurrentViewer().getContents();
456 			if (part != null) {
457 				IFigure referenceFigure = part.getContentPane();
458 				referenceFigure.removeFigureListener(this);
459 			}
460 		}
461 		super.setViewer(viewer);
462 
463 		if (getCurrentViewer() != null) {
464 			getCurrentViewer().getControl().addControlListener(this);
465 			GraphicalEditPart part = (GraphicalEditPart)getCurrentViewer().getContents();
466 			if (part != null) {
467 				IFigure referenceFigure = part.getContentPane();
468 				referenceFigure.addFigureListener(this);
469 			}
470 		}
471 	}
472 
473 	/*
474 	 * (non-Javadoc)
475 	 *
476 	 * @see org.eclipse.swt.events.ControlListener#controlMoved(org.eclipse.swt.events.ControlEvent)
477 	 */
478 	public void controlMoved(ControlEvent e) {
479 	}
480 
481 	/*
482 	 * (non-Javadoc)
483 	 *
484 	 * @see org.eclipse.swt.events.ControlListener#controlResized(org.eclipse.swt.events.ControlEvent)
485 	 */
486 	public void controlResized(ControlEvent e) {
487 	}
488 
489 	/*
490 	 * (non-Javadoc)
491 	 *
492 	 * @see org.eclipse.gef.tools.AbstractTool#viewerExited(org.eclipse.swt.events.MouseEvent,
493 	 * org.eclipse.gef.EditPartViewer)
494 	 */
495 	public void viewerExited(MouseEvent me, EditPartViewer viewer) {
496 		// //don't want to reset the viewer to null
497 		// if (viewer == getCurrentViewer()) {
498 		// handleViewerExited();
499 		// getCurrentInput().setInput(me);
500 		// }
501 		super.viewerExited(me, viewer);
502 	}
503 
504 	/**
505 	 * @param zoomManager
506 	 */
507 	public void setZoomManager(ZoomManager zoomManager) {
508 		if (this.currentManager != null) {
509 			this.currentManager.removeZoomListener(this);
510 			this.currentManager.getViewport().getHorizontalRangeModel().removePropertyChangeListener(this);
511 			this.currentManager.getViewport().getVerticalRangeModel().removePropertyChangeListener(this);
512 		}
513 		this.currentManager = zoomManager;
514 		if (this.currentManager != null) {
515 			this.currentManager.addZoomListener(this);
516 			this.currentManager.getViewport().getHorizontalRangeModel().addPropertyChangeListener(this);
517 			this.currentManager.getViewport().getVerticalRangeModel().addPropertyChangeListener(this);
518 			// make sure that everthing is up-to-date.
519 			IFigure referenceFigure = getReferenceFigure();
520 			if (referenceFigure != null)
521 				getCurrentViewer().flush();
522 		}
523 		updateViewPort();
524 	}
525 
526 	private IFigure getReferenceFigure() {
527 		GraphicalEditPart part = getReferenceEditPart();
528 		if (part != null)
529 			return part.getFigure();
530 		return null;
531 	}
532 
533 	/**
534 	 * @return
535 	 */
536 	private GraphicalEditPart getReferenceEditPart() {
537 		if (getCurrentViewer() == null)
538 			return null;
539 		if (getCurrentViewer().getContents() instanceof GraphicalEditPart) {
540 			return (GraphicalEditPart)getCurrentViewer().getContents();
541 		}
542 		return null;
543 	}
544 
545 	/*
546 	 * (non-Javadoc)
547 	 *
548 	 * @see org.eclipse.gef.editparts.ZoomListener#zoomChanged(double)
549 	 */
550 	public void zoomChanged(double zoom) {
551 		// Viewport viewport = this.currentManager.getViewport();
552 		// if (Math.abs(1-zoom) < .1) {
553 		// //set the bounds of the rectangle to zeros
554 		// getFeedBackFigure().setBounds(new Rectangle());
555 		// }
556 		// //adjust the feedback according to the zoom.
557 		// int x = viewport.getHorizontalRangeModel().getValue();
558 		// int width = viewport.getHorizontalRangeModel().getExtent();
559 		// int y = viewport.getVerticalRangeModel().getValue();
560 		// int height = viewport.getVerticalRangeModel().getExtent();
561 		// Rectangle newBounds = new Rectangle(x,y,width,height);
562 		// //translate it.
563 
564 	}
565 
566 	/*
567 	 * (non-Javadoc)
568 	 *
569 	 * @see java.beans.PropertyChangeListener#propertyChange(java.beans.PropertyChangeEvent)
570 	 */
571 	public void propertyChange(PropertyChangeEvent evt) {
572 		updateViewPort();
573 	}
574 
575 	/**
576 	 *
577 	 */
578 	private void updateViewPort() {
579 		if (this.currentManager == null)
580 			return;
581 		if (getCurrentViewer() == null)
582 			return;
583 		Viewport viewport = this.currentManager.getViewport();
584 		double zoom = this.currentManager.getZoom();
585 		// adjust the feedback according to the zoom.
586 		int x = viewport.getHorizontalRangeModel().getValue();
587 		int width = viewport.getHorizontalRangeModel().getExtent();
588 		int y = viewport.getVerticalRangeModel().getValue();
589 		int height = viewport.getVerticalRangeModel().getExtent();
590 		if (Math.abs(1 - zoom) < .1) {
591 			// set the bounds of the rectangle to zeros
592 			x += width / 2;
593 			y += height / 2;
594 			width = 0;
595 			height = 0;
596 		}
597 		Rectangle newBounds = new Rectangle(x, y, width, height);
598 		GraphicalEditPart part = (GraphicalEditPart)getCurrentViewer().getContents();
599 		if (part != null) {
600 			IFigure referenceFigure = part.getContentPane();
601 			newBounds.scale(1 / this.currentManager.getZoom());
602 			referenceFigure.translateToParent(newBounds);
603 			// adjust for the insets on the figure
604 			// newBounds.crop(getFeedBackFigure().getInsets());
605 			newBounds.shrink(1, 1);// getFeedBackFigure().getLineWidth(), getFeedBackFigure().getLineWidth());
606 			getFeedBackFigure().setBounds(newBounds);
607 		}
608 
609 	}
610 
611 	/*
612 	 * (non-Javadoc)
613 	 *
614 	 * @see org.eclipse.draw2d.FigureListener#figureMoved(org.eclipse.draw2d.IFigure)
615 	 */
616 	public void figureMoved(IFigure source) {
617 		updateViewPort();
618 
619 	}
620 
621 }