| /******************************************************************************* |
| * Copyright 2005-2006, CHISEL Group, University of Victoria, Victoria, BC, Canada. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * The Chisel Group, University of Victoria |
| *******************************************************************************/ |
| package org.eclipse.mylar.zest.core.internal.graphviewer.parts.tools; |
| |
| import java.beans.PropertyChangeEvent; |
| import java.beans.PropertyChangeListener; |
| |
| import org.eclipse.draw2d.ColorConstants; |
| import org.eclipse.draw2d.Cursors; |
| import org.eclipse.draw2d.FigureListener; |
| import org.eclipse.draw2d.IFigure; |
| import org.eclipse.draw2d.RectangleFigure; |
| import org.eclipse.draw2d.Viewport; |
| import org.eclipse.draw2d.geometry.Dimension; |
| import org.eclipse.draw2d.geometry.Point; |
| import org.eclipse.draw2d.geometry.Rectangle; |
| import org.eclipse.gef.DragTracker; |
| import org.eclipse.gef.EditPartViewer; |
| import org.eclipse.gef.GraphicalEditPart; |
| import org.eclipse.gef.RequestConstants; |
| import org.eclipse.gef.editparts.ZoomListener; |
| import org.eclipse.gef.editparts.ZoomManager; |
| import org.eclipse.gef.tools.AbstractTool; |
| import org.eclipse.swt.events.ControlEvent; |
| import org.eclipse.swt.events.ControlListener; |
| import org.eclipse.swt.events.MouseEvent; |
| |
| /** |
| * A tool that is used in the graph overview viewer that allows the user to select an area |
| * of the graph to zoom on. |
| * @author Del Myers |
| * |
| */ |
| public class OverviewRectangleTool extends AbstractTool implements DragTracker, ControlListener, ZoomListener, PropertyChangeListener, FigureListener { |
| private RectangleFigure focusFigure; |
| private int dragState; |
| private Rectangle startBounds; |
| private TargetShape target; |
| private FigureListener focusFigureListener; |
| private Rectangle referenceBounds; |
| private ZoomManager currentManager; |
| |
| private static final int OUTSIDE = 0; |
| private static final int INSIDE = 1<<1; |
| private static final int NORTH = INSIDE | 1<<2; |
| private static final int EAST = INSIDE |1<<3; |
| private static final int SOUTH = INSIDE | 1<<4; |
| private static final int WEST = INSIDE | 1<<5; |
| |
| private static final int NORTHWEST = INSIDE | NORTH | WEST; |
| private static final int NORTHEAST = INSIDE | NORTH | EAST; |
| private static final int SOUTHWEST = INSIDE | SOUTH | WEST; |
| private static final int SOUTHEAST = INSIDE | SOUTH | EAST; |
| |
| private class FocusMoveListener implements FigureListener { |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.draw2d.FigureListener#figureMoved(org.eclipse.draw2d.IFigure) |
| */ |
| public void figureMoved(IFigure source) { |
| //move the target to the center of the figure. |
| setTargetCenter(source.getBounds().getCenter()); |
| } |
| |
| } |
| |
| /** |
| * |
| */ |
| public OverviewRectangleTool() { |
| setUnloadWhenFinished(false); |
| referenceBounds = new Rectangle(); |
| //setEditDomain(new EditDomain()); |
| } |
| /* (non-Javadoc) |
| * @see org.eclipse.gef.tools.AbstractTool#handleDragStarted() |
| */ |
| protected boolean handleDragStarted() { |
| setState(STATE_DRAG); |
| setDragState(getRelativeCursorLocation(getStartLocation())); |
| Rectangle bounds = getFeedBackFigure().getBounds().getCopy(); |
| getFeedBackFigure().translateToAbsolute(bounds); |
| setDragStartBounds(bounds); |
| return true; |
| } |
| |
| /** |
| * @param bounds |
| */ |
| private void setDragStartBounds(Rectangle bounds) { |
| this.startBounds = bounds.getCopy(); |
| |
| } |
| |
| private Rectangle getDragStartBounds() { |
| return startBounds; |
| } |
| |
| private void setDragState(int state) { |
| this.dragState = state; |
| } |
| |
| /** |
| * @return the dragState |
| */ |
| private int getDragState() { |
| return dragState; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.gef.tools.AbstractTool#handleDragInProgress() |
| */ |
| protected boolean handleDragInProgress() { |
| setState(STATE_DRAG_IN_PROGRESS); |
| Dimension delta = getDragMoveDelta(); |
| Point start = getStartLocation(); |
| RectangleFigure feedBack = getFeedBackFigure(); |
| boolean moving = false; |
| int state = getDragState(); |
| Rectangle oldBounds = getDragStartBounds(); |
| Rectangle newBounds = oldBounds.getCopy(); |
| if (isNorth(state)) { |
| newBounds.y = oldBounds.y+delta.height; |
| newBounds.height = oldBounds.height-delta.height; |
| } else if (isSouth(state)) { |
| newBounds.height = oldBounds.height+delta.height; |
| } |
| if (isWest(state)) { |
| newBounds.x = oldBounds.x+delta.width; |
| newBounds.width = oldBounds.width-delta.width; |
| } else if (isEast(state)) { |
| newBounds.width = oldBounds.width+delta.width; |
| } |
| if (!(isWest(state) || isEast(state) || isNorth(state) || isSouth(state))) { |
| if (isInside(state)) { |
| moving = true; |
| newBounds.x = oldBounds.x+delta.width; |
| newBounds.y = oldBounds.y+delta.height; |
| } else { |
| //a new rectangle is drawn. |
| newBounds = new Rectangle(start, delta); |
| } |
| } |
| //adjust the bounds for negative widths and heights |
| if (newBounds.width < 0) { |
| newBounds.x += newBounds.width; |
| newBounds.width = -newBounds.width; |
| } |
| if (newBounds.height < 0) { |
| newBounds.y += newBounds.height; |
| newBounds.height = -newBounds.height; |
| } |
| int minX = getCurrentViewer().getControl().getBounds().x; |
| int minY = getCurrentViewer().getControl().getBounds().y; |
| int maxWidth = getCurrentViewer().getControl().getBounds().width; |
| int maxHeight = getCurrentViewer().getControl().getBounds().height; |
| if (newBounds.x < minX || newBounds.y < minY || |
| newBounds.x + newBounds.width > minX + maxWidth || |
| newBounds.y + newBounds.height > minY + maxHeight) { |
| if (moving) { |
| if (newBounds.x < minX) { |
| newBounds.x = minX; |
| } |
| if (newBounds.y < minY) { |
| newBounds.y = minY; |
| } |
| if (newBounds.x+newBounds.width > minX+maxWidth) { |
| newBounds.x = maxWidth-newBounds.width;; |
| } |
| if (newBounds.y +newBounds.height > minY+maxHeight) { |
| newBounds.y = maxHeight-newBounds.height; |
| } |
| } else { |
| if (newBounds.x < minX) { |
| newBounds.width = newBounds.x+newBounds.width-minX; |
| newBounds.x = minX; |
| } |
| if (newBounds.y < minY) { |
| newBounds.height = newBounds.y+newBounds.height-minY; |
| newBounds.y = minY; |
| } |
| if (newBounds.x+newBounds.width > minX+maxWidth) { |
| newBounds.width = maxWidth-newBounds.x; |
| } |
| if (newBounds.y +newBounds.height > minY+maxHeight) { |
| newBounds.height = maxHeight-newBounds.y; |
| } |
| } |
| } |
| feedBack.translateToRelative(newBounds); |
| feedBack.setBounds(newBounds); |
| return true; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.gef.tools.AbstractTool#handleMove() |
| */ |
| protected boolean handleMove() { |
| Point location = getLocation(); |
| switch (getRelativeCursorLocation(location)) { |
| case NORTH: |
| case SOUTH: |
| setCursor(Cursors.SIZENS); |
| break; |
| case EAST: |
| case WEST: |
| setCursor(Cursors.SIZEWE); |
| break; |
| case NORTHWEST: |
| case SOUTHEAST: |
| setCursor(Cursors.SIZENWSE); |
| break; |
| case NORTHEAST: |
| case SOUTHWEST: |
| setCursor(Cursors.SIZENESW); |
| break; |
| case INSIDE: |
| setCursor(Cursors.SIZEALL); |
| break; |
| default: |
| setCursor(getDefaultCursor()); |
| |
| } |
| return true; |
| } |
| /* (non-Javadoc) |
| * @see org.eclipse.gef.tools.AbstractTool#handleButtonUp(int) |
| */ |
| protected boolean handleButtonUp(int button) { |
| handleFinished(); |
| return true; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.gef.tools.AbstractTool#handleFinished() |
| */ |
| protected void handleFinished() { |
| if (isInState(STATE_DRAG_IN_PROGRESS) || isInState(STATE_DRAG)) { |
| |
| } else { |
| Point location = getLocation(); |
| getFeedBackFigure().setBounds(new Rectangle(location.x, location.y, 0,0)); |
| } |
| setState(STATE_TERMINAL); |
| resetFlags(); |
| zoomTo(getFeedBackFigure().getBounds().getCopy()); |
| |
| //super.handleFinished(); |
| } |
| |
| /** |
| * @param copy |
| */ |
| private void zoomTo(Rectangle rect) { |
| referenceBounds = rect.getCopy(); |
| GraphicalEditPart part = (GraphicalEditPart)getCurrentViewer().getContents(); |
| if (part != null) { |
| part.getFigure().translateFromParent(referenceBounds); |
| if (rect.isEmpty()) { |
| //make sure that it didn't scale to a size greater than 0. |
| referenceBounds.setSize(0,0); |
| } |
| if (this.currentManager != null) |
| this.currentManager.zoomTo(referenceBounds); |
| if (referenceBounds.isEmpty()) { |
| //reset the figure so as to not jar the user. |
| getFeedBackFigure().setBounds(rect); |
| } |
| } |
| } |
| boolean isNorth(int loc) { |
| loc = (isInside(loc)) ? loc ^ INSIDE : loc; |
| return (loc & NORTH) != 0; |
| } |
| |
| boolean isEast(int loc) { |
| loc = (isInside(loc)) ? loc ^ INSIDE : loc; |
| return (loc & EAST)!= 0; |
| } |
| |
| boolean isSouth(int loc) { |
| loc = (isInside(loc)) ? loc ^ INSIDE : loc; |
| return (loc & SOUTH) != 0; |
| } |
| |
| boolean isWest(int loc) { |
| loc = (isInside(loc)) ? loc ^ INSIDE : loc; |
| return (loc & WEST) != 0; |
| } |
| |
| boolean isInside(int loc) { |
| return (loc & INSIDE) != 0; |
| } |
| |
| private int getRelativeCursorLocation(Point location) { |
| RectangleFigure feedBack = getFeedBackFigure(); |
| Rectangle bounds = feedBack.getBounds(); |
| location = location.getCopy(); |
| feedBack.translateToRelative(location); |
| int result = OUTSIDE; |
| if (feedBack.containsPoint(location)) { |
| result |= INSIDE; |
| //check to see the bounds to find out which |
| //cursor to use |
| if (Math.abs(location.y - bounds.y)<=2) { |
| result |= NORTH; |
| } else if (Math.abs(location.y -(bounds.y+bounds.height-1))<=2) { |
| result |= SOUTH; |
| } |
| if (Math.abs(location.x - bounds.x)<=2) { |
| result |= WEST; |
| } else if (Math.abs(location.x - (bounds.x+bounds.width-1))<=2) { |
| result |= EAST; |
| } |
| } |
| return result; |
| } |
| |
| private RectangleFigure getFeedBackFigure() { |
| if (focusFigure == null) { |
| focusFigure = new RectangleFigure(); |
| focusFigure.setForegroundColor(ColorConstants.red); |
| focusFigure.setFill(false); |
| focusFigure.setLineWidth(2); |
| focusFigureListener = new FocusMoveListener(); |
| focusFigure.addFigureListener(focusFigureListener); |
| focusFigure.setBounds(new Rectangle()); |
| addFeedback(focusFigure); |
| // referenceBounds = focusFigure.getParent().getBounds().getCopy(); |
| // focusFigure.getParent().addFigureListener(this); |
| } |
| return focusFigure; |
| } |
| |
| private TargetShape getTargetFigure() { |
| if (target == null) { |
| target = new TargetShape(); |
| target.setForegroundColor(ColorConstants.red); |
| target.setFill(false); |
| target.setBounds(new Rectangle(-10,-10,20,20)); |
| target.setLineWidth(2); |
| addFeedback(target); |
| } |
| return target; |
| } |
| |
| private void setTargetCenter(Point center) { |
| Point newCenter = center.getCopy(); |
| newCenter.translate(-getTargetFigure().getSize().width/2, -getTargetFigure().getSize().height/2); |
| getTargetFigure().setLocation(newCenter); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.gef.tools.AbstractTool#deactivate() |
| */ |
| public void deactivate() { |
| if (focusFigure != null) { |
| focusFigure.removeFigureListener(focusFigureListener); |
| removeFeedback(focusFigure); |
| focusFigure = null; |
| } |
| if (target != null) { |
| removeFeedback(target); |
| target = null; |
| } |
| if (getCurrentViewer() != null) { |
| getCurrentViewer().getControl().removeControlListener(this); |
| if (getCurrentViewer().getContents() instanceof GraphicalEditPart) { |
| ((GraphicalEditPart)getCurrentViewer().getContents()).getFigure().removeFigureListener(this); |
| } |
| } |
| if (this.currentManager != null) { |
| this.currentManager.removeZoomListener(this); |
| this.currentManager.getViewport().getHorizontalRangeModel().removePropertyChangeListener(this); |
| this.currentManager.getViewport().getVerticalRangeModel().removePropertyChangeListener(this); |
| } |
| super.deactivate(); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.gef.tools.AbstractTool#activate() |
| */ |
| public void activate() { |
| |
| super.activate(); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.gef.tools.AbstractTool#getCommandName() |
| */ |
| protected String getCommandName() { |
| return RequestConstants.REQ_SELECTION; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.gef.tools.AbstractTool#setViewer(org.eclipse.gef.EditPartViewer) |
| */ |
| public void setViewer(EditPartViewer viewer) { |
| if (getCurrentViewer() != null) { |
| getCurrentViewer().getControl().removeControlListener(this); |
| GraphicalEditPart part = (GraphicalEditPart) getCurrentViewer().getContents(); |
| if (part != null) { |
| IFigure referenceFigure = part.getContentPane(); |
| referenceFigure.removeFigureListener(this); |
| } |
| } |
| super.setViewer(viewer); |
| |
| if (getCurrentViewer() != null) { |
| getCurrentViewer().getControl().addControlListener(this); |
| GraphicalEditPart part = (GraphicalEditPart) getCurrentViewer().getContents(); |
| if (part != null) { |
| IFigure referenceFigure = part.getContentPane(); |
| referenceFigure.addFigureListener(this); |
| } |
| } |
| } |
| /* (non-Javadoc) |
| * @see org.eclipse.swt.events.ControlListener#controlMoved(org.eclipse.swt.events.ControlEvent) |
| */ |
| public void controlMoved(ControlEvent e) {} |
| /* (non-Javadoc) |
| * @see org.eclipse.swt.events.ControlListener#controlResized(org.eclipse.swt.events.ControlEvent) |
| */ |
| public void controlResized(ControlEvent e) {} |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.gef.tools.AbstractTool#viewerExited(org.eclipse.swt.events.MouseEvent, org.eclipse.gef.EditPartViewer) |
| */ |
| public void viewerExited(MouseEvent me, EditPartViewer viewer) { |
| // //don't want to reset the viewer to null |
| // if (viewer == getCurrentViewer()) { |
| // handleViewerExited(); |
| // getCurrentInput().setInput(me); |
| // } |
| super.viewerExited(me, viewer); |
| } |
| /** |
| * @param zoomManager |
| */ |
| public void setZoomManager(ZoomManager zoomManager) { |
| if (this.currentManager != null) { |
| this.currentManager.removeZoomListener(this); |
| this.currentManager.getViewport().getHorizontalRangeModel().removePropertyChangeListener(this); |
| this.currentManager.getViewport().getVerticalRangeModel().removePropertyChangeListener(this); |
| } |
| this.currentManager = zoomManager; |
| if (this.currentManager != null) { |
| this.currentManager.addZoomListener(this); |
| this.currentManager.getViewport().getHorizontalRangeModel().addPropertyChangeListener(this); |
| this.currentManager.getViewport().getVerticalRangeModel().addPropertyChangeListener(this); |
| //make sure that everthing is up-to-date. |
| IFigure referenceFigure = getReferenceFigure(); |
| if (referenceFigure != null) |
| getCurrentViewer().flush(); |
| } |
| updateViewPort(); |
| } |
| |
| private IFigure getReferenceFigure() { |
| GraphicalEditPart part = getReferenceEditPart(); |
| if (part != null) |
| return part.getFigure(); |
| return null; |
| } |
| |
| |
| /** |
| * @return |
| */ |
| private GraphicalEditPart getReferenceEditPart() { |
| if (getCurrentViewer() == null) return null; |
| if (getCurrentViewer().getContents() instanceof GraphicalEditPart) { |
| return (GraphicalEditPart) getCurrentViewer().getContents(); |
| } |
| return null; |
| } |
| /* (non-Javadoc) |
| * @see org.eclipse.gef.editparts.ZoomListener#zoomChanged(double) |
| */ |
| public void zoomChanged(double zoom) { |
| // Viewport viewport = this.currentManager.getViewport(); |
| // if (Math.abs(1-zoom) < .1) { |
| // //set the bounds of the rectangle to zeros |
| // getFeedBackFigure().setBounds(new Rectangle()); |
| // } |
| // //adjust the feedback according to the zoom. |
| // int x = viewport.getHorizontalRangeModel().getValue(); |
| // int width = viewport.getHorizontalRangeModel().getExtent(); |
| // int y = viewport.getVerticalRangeModel().getValue(); |
| // int height = viewport.getVerticalRangeModel().getExtent(); |
| // Rectangle newBounds = new Rectangle(x,y,width,height); |
| // //translate it. |
| |
| |
| } |
| |
| /* (non-Javadoc) |
| * @see java.beans.PropertyChangeListener#propertyChange(java.beans.PropertyChangeEvent) |
| */ |
| public void propertyChange(PropertyChangeEvent evt) { |
| updateViewPort(); |
| } |
| /** |
| * |
| */ |
| private void updateViewPort() { |
| if (this.currentManager == null) return; |
| if (getCurrentViewer() == null) return; |
| Viewport viewport = this.currentManager.getViewport(); |
| double zoom = this.currentManager.getZoom(); |
| // adjust the feedback according to the zoom. |
| int x = viewport.getHorizontalRangeModel().getValue(); |
| int width = viewport.getHorizontalRangeModel().getExtent(); |
| int y = viewport.getVerticalRangeModel().getValue(); |
| int height = viewport.getVerticalRangeModel().getExtent(); |
| if (Math.abs(1-zoom) < .1) { |
| //set the bounds of the rectangle to zeros |
| x+= width/2; |
| y+= height/2; |
| width = 0; |
| height = 0; |
| } |
| Rectangle newBounds = new Rectangle(x,y,width,height); |
| GraphicalEditPart part = (GraphicalEditPart) getCurrentViewer().getContents(); |
| if (part != null) { |
| IFigure referenceFigure = part.getContentPane(); |
| newBounds.scale(1/this.currentManager.getZoom()); |
| referenceFigure.translateToParent(newBounds); |
| //adjust for the insets on the figure |
| //newBounds.crop(getFeedBackFigure().getInsets()); |
| newBounds.shrink(1,1);//getFeedBackFigure().getLineWidth(), getFeedBackFigure().getLineWidth()); |
| getFeedBackFigure().setBounds(newBounds); |
| } |
| |
| } |
| /* (non-Javadoc) |
| * @see org.eclipse.draw2d.FigureListener#figureMoved(org.eclipse.draw2d.IFigure) |
| */ |
| public void figureMoved(IFigure source) { |
| updateViewPort(); |
| |
| } |
| |
| |
| } |