/******************************************************************************* * Copyright (c) 2008, 2012 Stepan Rutz. * 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: * Stepan Rutz - initial implementation * Hallvard Trætteberg - further cleanup and development *******************************************************************************/ package org.eclipse.nebula.widgets.geomap.internal; import org.eclipse.nebula.widgets.geomap.GeoMapUtil; import org.eclipse.swt.SWT; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.events.MouseListener; import org.eclipse.swt.events.MouseMoveListener; import org.eclipse.swt.events.MouseTrackListener; import org.eclipse.swt.events.MouseWheelListener; import org.eclipse.swt.events.PaintEvent; import org.eclipse.swt.events.PaintListener; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; /** * Implements default interactive behavior, with support for panning and zooming. * @since 3.3 * */ public abstract class DefaultMouseHandler implements MouseListener, MouseWheelListener, MouseMoveListener, MouseTrackListener, PaintListener { /** * */ private final GeoMapPositioned geoMap; /** * @param geoMap * @param control */ public DefaultMouseHandler(GeoMapPositioned geoMap) { this.geoMap = geoMap; } /** * @return */ protected GeoMapPositioned getGeoMap() { return this.geoMap; } /** * Zoom in at cursor position * @param e the MouseEvent */ protected void zoomIn(MouseEvent e) { GeoMapUtil.zoomIn(getGeoMap(), new Point(e.x, e.y)); } /** * Zoom out at cursor position * @param e the MouseEvent */ protected void zoomOut(MouseEvent e) { GeoMapUtil.zoomOut(getGeoMap(), new Point(e.x, e.y)); } /** * Sets the map position * @param x the x or x offset * @param y the y or y offset * @param relative tells whether x and y are offsets */ protected void pan(int x, int y, boolean relative) { if (relative) { Point p = getGeoMap().getMapPosition(); x += p.x; y += p.y; } getGeoMap().setMapPosition(x, y); } /** * Gets the size of the map viewport/pane. * @return the size of the map viewport/pane */ public abstract Point getMapSize(); /** * Center at cursor position * @param e the MouseEvent */ protected void center(MouseEvent e) { Point size = getMapSize(); Point mapPosition = getGeoMap().getMapPosition(); getGeoMap().setMapPosition(mapPosition.x + e.x - size.x / 2, mapPosition.y + e.y - size.y / 2); } // /** * @return Returns the zoomClickCount. */ public int getZoomClickCount() { return zoomClickCount; } /** * Sets the number of clicks that triggers a zoom. * @param zoomClickCount The zoomClickCount to set. */ public void setZoomClickCount(int zoomClickCount) { this.zoomClickCount = zoomClickCount; } /** * @return Returns the panCenterButtons. */ public int getPanCenterButtons() { return panCenterButtons; } /** * @param panCenterButtons The panCenterButtons to set. */ public void setPanCenterButtons(int panCenterButtons) { this.panCenterButtons = panCenterButtons; } /** * @return Returns the zoomInClickButtons. */ public int getZoomInClickButtons() { return zoomInClickButtons; } /** * Sets the button(s) that triggers a zoom in. * @param zoomInClickButtons The zoomInClickButtons to set. */ public void setZoomInClickButtons(int zoomInClickButtons) { this.zoomInClickButtons = zoomInClickButtons; } /** * @return Returns the zoomOutClickButtons. */ public int getZoomOutClickButtons() { return zoomOutClickButtons; } /** * Sets the button(s) that triggers a zoom out. * @param zoomOutClickButtons The zoomOutClickButtons to set. */ public void setZoomOutClickButtons(int zoomOutClickButtons) { this.zoomOutClickButtons = zoomOutClickButtons; } /** * @return Returns the panClickCount. */ public int getPanClickCount() { return panClickCount; } /** * Sets the number of clicks that triggers a pan. * @param panClickCount The panClickCount to set. */ public void setPanClickCount(int panClickCount) { this.panClickCount = panClickCount; } /** * @return Returns the panButtons. */ public int getPanButtons() { return panButtons; } /** * Sets the button(s) that triggers a pan. * @param panButtons The panButtons to set. */ public void setPanButtons(int panButtons) { this.panButtons = panButtons; } /** * @return Returns the panScrollButtons. */ public int getPanScrollButtons() { return panScrollButtons; } /** * Sets the button(s) that triggers a pan, when using the scroll wheel. * @param panScrollButtons The panScrollButtons to set. */ public void setPanScrollButtons(int panScrollButtons) { this.panScrollButtons = panScrollButtons; } /** * @return Returns the panScrollSpeed. */ public int getPanScrollSpeed() { return panScrollSpeed; } /** * Sets the panning speed, when using the scroll wheel. * @param panScrollSpeed The panScrollSpeed to set. */ public void setPanScrollSpeed(int panScrollSpeed) { this.panScrollSpeed = panScrollSpeed; } /** * @return Returns the zoomScrollButtons. */ public int getZoomScrollButtons() { return zoomScrollButtons; } /** * Sets the button(s) that triggers a zoom, when using the scroll wheel. * @param zoomScrollButtons The zoomScrollButtons to set. */ public void setZoomScrollButtons(int zoomScrollButtons) { this.zoomScrollButtons = zoomScrollButtons; } /** * @return Returns the zoomRectangleButtons. */ public int getZoomRectangleButtons() { return zoomRectangleButtons; } /** * Sets the button(s) that triggers a zoom (rectangle). * @param zoomRectangleButtons The zoomRectangleButtons to set. */ public void setZoomRectangleButtons(int zoomRectangleButtons) { this.zoomRectangleButtons = zoomRectangleButtons; } // private Point panStart; private Point downPosition; public void mouseEnter(MouseEvent e) { // control.forceFocus(); } public void mouseExit(MouseEvent e) { // ignore } public void mouseHover(MouseEvent e) { // ignore } /** * Checks that the MouseEvent corresponds to the provided buttons bit mask. * The buttons are or'ed button bits for modifiers keys and mouse buttons. * @param e the MouseEvent * @param buttons Or'ed button bits for modifiers keys and mouse buttons. * @return true if the MouseEvent corresponds to the provided buttons, false otherwise */ protected boolean checkButtons(MouseEvent e, int buttons) { int mask = e.stateMask; switch (e.button) { case 1: mask |= SWT.BUTTON1; break; case 2: mask |= SWT.BUTTON2; break; case 3: mask |= SWT.BUTTON3; break; case 4: mask |= SWT.BUTTON4; break; case 5: mask |= SWT.BUTTON5; break; } return mask == buttons; } public void mouseDown(MouseEvent e) { handleDown(e); } public void mouseMove(MouseEvent e) { if (isPanning()) { handlePanDrag(e); } else if (zoomStart != null) { handleZoomDrag(e); } } public void mouseDoubleClick(MouseEvent e) { handleZoomClick(e); } private int zoomClickCount = 1; private int zoomInClickButtons = SWT.BUTTON1; private int zoomOutClickButtons = SWT.BUTTON3; private int zoomRectangleButtons = SWT.BUTTON1 | SWT.SHIFT; private Point zoomStart = null; private Rectangle zoomRectangle = null; /** * Checks if a click event is a zoom and performs it. * @param e the MouseEvent * @return if the click event is a zoom */ protected boolean handleZoomClick(MouseEvent e) { if (e.count != zoomClickCount) { return false; } if (checkButtons(e, zoomInClickButtons)) { zoomIn(e); return true; } else if (checkButtons(e, zoomOutClickButtons)) { zoomOut(e); return true; } return false; } private int panClickCount = 1; private int panButtons = SWT.BUTTON1; private int panCenterButtons = SWT.BUTTON1 | SWT.CTRL; /** * Checks if a down event is (the start of) a pan or zoom and initiates it. * @param e the MouseEvent * @return if the click event is a zoom */ protected boolean handleDown(MouseEvent e) { if (e.count != panClickCount) { return false; } if (checkButtons(e, panCenterButtons)) { center(e); return true; } if (isPanStart(e)) { return panStart(e); } else if (isZoomStart(e)) { return zoomStart(e); } return false; } /** * @param e the MouseEvent * @return if the MouseEvent is considered start of a pan */ protected boolean isPanStart(MouseEvent e) { return checkButtons(e, panButtons); } /** * @param e the MouseEvent * @return if the MouseEvent is considered start of a zoom */ protected boolean isZoomStart(MouseEvent e) { return checkButtons(e, zoomRectangleButtons); } /** * Initiates a pan. * @param e the MouseEvent * @return if pan was really initiated */ protected boolean panStart(MouseEvent e) { panStart = new Point(e.x, e.y); downPosition = getGeoMap().getMapPosition(); return true; } /** * @return if a pan has been initiated. */ protected boolean isPanning() { return panStart != null; } /** * Initiates a zoom (rectangle). * @param e the MouseEvent * @return if zoom was really initiated */ protected boolean zoomStart(MouseEvent e) { zoomStart = new Point(e.x, e.y); return true; } /** * @return if a zoom has been initiated. */ protected boolean isZooming() { return zoomStart != null; } public void mouseUp(MouseEvent e) { boolean consumed = false; if (isPanning() && handlePanUp(e)) { consumed = true; } else if (isZooming() && handleZoomUp(e)) { consumed = true; } if (! consumed) { handleZoomClick(e); } } private int panScrollButtons = SWT.NONE; private int panScrollSpeed = 15; private int zoomScrollButtons = SWT.NONE; public void mouseScrolled(MouseEvent e) { if (e.count > 0 && checkButtons(e, zoomScrollButtons)) { zoomIn(e); } else if (e.count < 0 && checkButtons(e, zoomScrollButtons)) { zoomOut(e); } else if (checkButtons(e, panScrollButtons)) { pan(e.count * panScrollSpeed, 0, true); } } /** * Handles one pan step, according to the distance from the click to the current position * @param e the MouseEvent * @return if pan was active and the movement offset large enough */ protected boolean handlePanDrag(MouseEvent e) { if (isPanning()) { int dx = panStart.x - e.x, dy = panStart.y - e.y; if (dx * dx + dy * dy >= 4) { pan(downPosition.x + dx, downPosition.y + dy, false); return true; } } return false; } /** * Handles one zoom step, extending the zoom rectangle. * @param e the MouseEvent * @return if zoom was active */ protected boolean handleZoomDrag(MouseEvent e) { if (isZooming()) { int minX = Math.min(zoomStart.x, e.x), minY = Math.min(zoomStart.y, e.y); int maxX = Math.max(zoomStart.x, e.x), maxY = Math.max(zoomStart.y, e.y); Point mapPosition = geoMap.getMapPosition(); zoomRectangle = new Rectangle(mapPosition.x + minX, mapPosition.y + minY, maxX - minX, maxY - minY); return true; } return false; } /** * Handles end of pan. * @param e the MouseEvent * @return if pan was active */ protected boolean handlePanUp(MouseEvent e) { boolean result = handlePanDrag(e); panStart = null; downPosition = null; return result; } /** * Handles zooming to rectangle. * @param e the MouseEvent * @return if zoom was active */ protected boolean handleZoomUp(MouseEvent e) { if (isZooming()) { if (zoomRectangle != null && zoomRectangle.width >= 2 && zoomRectangle.height >= 2) { GeoMapUtil.zoomTo(getGeoMap(), getMapSize(), zoomRectangle, -1); } zoomStart = null; zoomRectangle = null; return true; } return false; } public void paintControl(PaintEvent e) { if (zoomRectangle != null) { Point mapPosition = geoMap.getMapPosition(); e.gc.drawRectangle(zoomRectangle.x - mapPosition.x, zoomRectangle.y - mapPosition.y, zoomRectangle.width, zoomRectangle.height); } } }