/*******************************************************************************
* Copyright (c) 2012, 2013 Tilera Corporation and others.
* 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:
* William R. Swanson (Tilera Corporation) - initial API and implementation
* Marc Dumais (Ericsson) : Bug 405735
*******************************************************************************/
package org.eclipse.cdt.visualizer.ui.util;
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.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Control;
/**
* Monitors mouse down/move/up events on a specified control (e.g. a canvas)
* and converts them to semantic events (click, double-click, select, drag, etc.)
*/
public class MouseMonitor
{
// --- constants ---
/** MouseEvent button ID for Left mouse button */
public static final int LEFT_BUTTON = 1;
/** MouseEvent button ID for Middle mouse button */
public static final int MIDDLE_BUTTON = 2;
/** MouseEvent button ID for Right mouse button */
public static final int RIGHT_BUTTON = 3;
/** Mouse drag state value */
public static final int MOUSE_DRAG_BEGIN = 0;
/** Mouse drag state value */
public static final int MOUSE_DRAG = 1;
/** Mouse drag state value */
public static final int MOUSE_DRAG_END = 2;
/** Distance mouse must move for a mouse-down to be treated as a drag */
public static final int MOUSE_DRAG_HYSTERESIS = 4;
// --- members ---
/** Control being monitored */
protected Control m_control = null;
/** Mouse button listener */
protected MouseListener m_mouseButtonListener = null;
/** Mouse move listener */
protected MouseMoveListener m_mouseMoveListener = null;
/** Mouse enter/exit event listener */
protected MouseTrackListener m_mouseTrackListener = null;
/** Whether mouse button is down. */
protected boolean m_mouseDown = false;
/** Whether mouse is being dragged. */
protected boolean m_mouseDrag = false;
/** Mouse-down point. */
protected Point m_mouseDownPoint = new Point(0,0);
/** Last button down. */
protected int m_mouseDownButton = 0;
/** Current mouse drag point. */
protected Point m_dragPoint = new Point(0,0);
/** Drag region. */
protected Rectangle m_dragRegion = new Rectangle(0,0,0,0);
// --- constructors/destructors ---
/** Constructor. */
public MouseMonitor() {
}
/** Constructor. */
public MouseMonitor(Control control) {
this();
m_control = control;
attach(m_control);
}
/** Dispose method. */
public void dispose() {
detach(m_control);
}
// --- init methods ---
/** Attach event listeners to specified control. */
protected void attach(Control control) {
detach(m_control);
control.addMouseListener(
m_mouseButtonListener = new MouseListener() {
public void mouseDown(MouseEvent e) {
mouseDownHandler(e.button, e.x, e.y, e.stateMask);
}
public void mouseUp(MouseEvent e) {
mouseUpHandler(e.button, e.x, e.y, e.stateMask);
}
public void mouseDoubleClick(MouseEvent e) {
mouseDoubleClickHandler(e.button, e.x, e.y, e.stateMask);
}
}
);
control.addMouseMoveListener(
m_mouseMoveListener = new MouseMoveListener() {
public void mouseMove(MouseEvent e) {
mouseMoveHandler(e.x, e.y, e.stateMask);
}
}
);
control.addMouseTrackListener(
m_mouseTrackListener = new MouseTrackListener() {
public void mouseEnter(MouseEvent e) {
mouseEnterHandler(e.x, e.y);
}
public void mouseExit(MouseEvent e) {
mouseExitHandler(e.x, e.y);
}
public void mouseHover(MouseEvent e) {
mouseHoverHandler(e.x, e.y);
}
}
);
}
/** Detach event listeners from specified control. */
protected void detach(Control control) {
if (control == null) return;
if (m_control != null) {
if (m_mouseButtonListener != null) {
m_control.removeMouseListener(m_mouseButtonListener);
m_mouseButtonListener = null;
}
if (m_mouseMoveListener != null) {
m_control.removeMouseMoveListener(m_mouseMoveListener);
m_mouseMoveListener = null;
}
if (m_mouseTrackListener != null) {
m_control.removeMouseTrackListener(m_mouseTrackListener);
m_mouseTrackListener = null;
}
}
}
// --- accessors ---
/** Gets associated control */
public Control getControl() {
return m_control;
}
/** Sets associated control */
public void setControl(Control control) {
detach(m_control);
m_control = control;
attach(m_control);
}
/** Gets mouse down point of current drag, if any */
public Point getMouseDownPoint() {
return m_mouseDownPoint;
}
/** Gets current drag x,y point. */
public Point getDragPoint() {
return m_dragPoint;
}
/** Gets bounds of most recent drag region. */
public Rectangle getDragRegion() {
return m_dragRegion;
}
// --- utilities ---
/** Returns true if either Shift key is down in mouse event modifier key mask. */
public static boolean isShiftDown(int keys) {
return ((keys & SWT.SHIFT) != 0);
}
/** Returns true if either Control key is down in mouse event modifier key mask. */
public static boolean isControlDown(int keys) {
return ((keys & SWT.CONTROL) != 0);
}
/** Returns true if either Alt key is down in mouse event modifier key mask. */
public static boolean isAltDown(int keys) {
return ((keys & SWT.ALT) != 0);
}
// --- methods ---
/** Internal -- sets drag point */
protected void setDragPoint(int x, int y) {
m_dragPoint.x=x;
m_dragPoint.y=y;
}
/** Internal -- sets drag region explicitly */
protected void setDragRegion(int x, int y, int width, int height) {
m_dragRegion.x=x;
m_dragRegion.y=y;
m_dragRegion.width=width;
m_dragRegion.height=height;
}
/** Internal -- sets drag region from specified drag start/end points */
protected void setDragRegionFromPoints(int x1, int y1, int x2, int y2) {
if (x1 < x2) {
m_dragRegion.x = x1;
m_dragRegion.width = x2 - x1;
}
else {
m_dragRegion.x = x2;
m_dragRegion.width = x1 - x2;
}
if (y1 < y2) {
m_dragRegion.y = y1;
m_dragRegion.height = y2 - y1;
}
else {
m_dragRegion.y = y2;
m_dragRegion.height = y1 - y2;
}
}
/** Invoked when mouse button is pressed */
protected void mouseDownHandler(int button, int x, int y, int keys) {
// Drag not applicable to right-click
if (! m_mouseDown && button != RIGHT_BUTTON) {
m_mouseDown = true;
m_mouseDownPoint.x = x;
m_mouseDownPoint.y = y;
m_mouseDownButton = button;
setDragPoint(x,y);
setDragRegion(x,y,0,0);
}
mouseDown(button, x, y, keys);
}
/** Invoked when mouse is moved */
protected void mouseMoveHandler(int x, int y, int keys) {
if (m_mouseDown) {
if (! m_mouseDrag) {
// allow a small hysteresis before we start dragging, so clicks with a little movement don't cause drags
int distance = Math.abs(x - m_mouseDownPoint.x) + Math.abs(y - m_mouseDownPoint.y);
if (distance > MOUSE_DRAG_HYSTERESIS) {
m_mouseDrag = true;
// initialize mouse drag
drag(m_mouseDownButton, m_mouseDownPoint.x, m_mouseDownPoint.y, keys, MOUSE_DRAG_BEGIN);
}
}
if (m_mouseDrag) {
// update mouse drag
int dx = x - m_mouseDownPoint.x;
int dy = y - m_mouseDownPoint.y;
setDragPoint(x,y);
setDragRegionFromPoints(m_mouseDownPoint.x, m_mouseDownPoint.y, x, y);
drag(m_mouseDownButton, dx, dy, keys, MOUSE_DRAG);
}
}
mouseMove(x, y, keys);
}
/** Invoked when mouse button is released */
protected void mouseUpHandler(int button, int x, int y, int keys) {
if (m_mouseDown) {
if (m_mouseDrag) {
// finish mouse drag
int dx = x - m_mouseDownPoint.x;
int dy = y - m_mouseDownPoint.y;
setDragPoint(x,y);
setDragRegionFromPoints(m_mouseDownPoint.x, m_mouseDownPoint.y, x, y);
drag(m_mouseDownButton, dx, dy, keys, MOUSE_DRAG_END);
m_mouseDrag = false;
}
else {
if (button == RIGHT_BUTTON) {
contextMenu(x, y, keys);
}
else {
select(x, y, keys);
}
}
m_mouseDown = false;
}
mouseUp(button, x, y, keys);
}
/** Invoked when mouse button is double-clicked */
protected void mouseDoubleClickHandler(int button, int x, int y, int keys) {
mouseDoubleClick(button, x, y, keys);
}
/** Invoked when mouse pointer enters control region */
protected void mouseEnterHandler(int x, int y) {
if (! m_mouseDown) {
mouseEnter(x, y);
}
}
/** Invoked when mouse pointer exits control region */
protected void mouseExitHandler(int x, int y) {
if (! m_mouseDown) {
mouseExit(x, y);
}
}
/** Invoked when mouse pointer hovers over control */
protected void mouseHoverHandler(int x, int y) {
if (! m_mouseDown) {
mouseHover(x, y);
}
}
// --- event handlers ---
// These are intended to be overridden by derived types.
// A user of this class need only override methods for events
// that need to be tracked.
/** Invoked when mouse button is pressed */
public void mouseDown(int button, int x, int y, int keys) {}
/** Invoked when mouse is moved */
public void mouseMove(int x, int y, int keys) {}
/** Invoked when mouse button is released */
public void mouseUp(int button, int x, int y, int keys) {}
/** Invoked for a selection click at the specified point. */
public void select(int x, int y, int keys) {}
/** Invoked for a context menu click at the specified point. */
public void contextMenu(int x, int y, int keys) {}
/** Invoked when mouse button is double-clicked */
public void mouseDoubleClick(int button, int x, int y, int keys) {}
/** Invoked when mouse is dragged (moved with mouse button down).
* Drag state indicates stage of drag:
* - MOUSE_DRAG_BEGIN -- dx, dy offset from initial mouse down point (initial mouse down)
* - MOUSE_DRAG -- dx, dy of intermediate drag offset (initial mouse down, then each mouse move)
* - MOUSE_DRAG_END -- dx, dy of final drag offset (mouse up)
* The pattern of calls is always: BEGIN, DRAG(+), END.
*/
public void drag(int button, int x, int y, int keys, int dragState) {}
/** Invoked when mouse pointer enters control region */
public void mouseEnter(int x, int y) {}
/** Invoked when mouse pointer exits control region */
public void mouseExit(int x, int y) {}
/** Invoked when mouse pointer hovers over control */
public void mouseHover(int x, int y) {}
}