/* * Copyright (c) 2002-@year@, University of Maryland * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are permitted provided * that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, this list of conditions * and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, this list of conditions * and the following disclaimer in the documentation and/or other materials provided with the * distribution. * * Neither the name of the University of Maryland nor the names of its contributors may be used to * endorse or promote products derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * Piccolo was written at the Human-Computer Interaction Laboratory www.cs.umd.edu/hcil by Jesse Grosjean * under the supervision of Ben Bederson. The Piccolo website is www.cs.umd.edu/hcil/piccolo. */ package edu.umd.cs.piccolo.event; import java.awt.Cursor; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import java.awt.event.MouseWheelEvent; import java.awt.geom.Point2D; import javax.swing.SwingUtilities; import edu.umd.cs.piccolo.PCamera; import edu.umd.cs.piccolo.PComponent; import edu.umd.cs.piccolo.PInputManager; import edu.umd.cs.piccolo.PNode; import edu.umd.cs.piccolo.util.PDimension; import edu.umd.cs.piccolo.util.PPickPath; /** * <b>PInputEvent</b> is used to notify PInputEventListeners of keyboard and mouse * input. It has methods for normal event properties such as event modifier keys * and event canvas location. * <P> * In addition is has methods to get the mouse position and delta in a variety * of coordinate systems. * <P> * Last of all it provides access to the dispatch manager that can be queried * to find the current mouse over, mouse focus, and keyboard focus. * <P> * @version 1.0 * @author Jesse Grosjean */ public class PInputEvent { private InputEvent inputEvent; private PPickPath pickPath; private PInputManager inputManager; private boolean handled; public PInputEvent(PInputManager inputManager, InputEvent event) { super(); inputEvent = event; this.inputManager = inputManager; } public void pushCursor(Cursor cursor) { PComponent component = getTopCamera().getComponent(); component.pushCursor(cursor); } public void popCursor() { PComponent component = getTopCamera().getComponent(); component.popCursor(); } //**************************************************************** // Accessing Picked Objects - Methods to access the objects associated // with this event. // <p> // Cameras can view layers that have // other cameras on them, so events may be arriving through a stack // of many cameras. The getCamera() method returns the bottommost // camera on that stack. The getTopCamera method returns the topmost // camera on that stack, this is also the camera through which the // event originated. //**************************************************************** /** * Return the bottom most camera that is currently painting. If you are * using internal cameras this may be different then what is returned by * getTopCamera. */ public PCamera getCamera() { return getPath().getBottomCamera(); } /** * Return the topmost camera this is painting. This is the camera assocaited * with the PCanvas that requested the current repaint. */ public PCamera getTopCamera() { return getPath().getTopCamera(); } /** * Get the canvas associated with the top camera. This is the canvas where the * originating swing event came from. */ public PComponent getComponent() { return getTopCamera().getComponent(); } /** * Return the input manager that dispatched this event. You can use this input * manager to find the current mouse focus, mouse over, and key focus nodes. * You can also set a new key focus node. */ public PInputManager getInputManager() { return inputManager; } /** * Return the PPickPath associated with this input event. */ public PPickPath getPath() { return pickPath; } public void setPath(PPickPath path) { pickPath = path; } /** * Return the bottom node on the current pickpath, that is the picked node * furthest from the root node. */ public PNode getPickedNode() { return pickPath.getPickedNode(); } //**************************************************************** // Basics //**************************************************************** public int getKeyCode() { if (isKeyEvent()) { KeyEvent e = (KeyEvent) inputEvent; return e.getKeyCode(); } throw new IllegalStateException("Can't get keycode from mouse event"); } public char getKeyChar() { if (isKeyEvent()) { KeyEvent e = (KeyEvent) inputEvent; return e.getKeyChar(); } throw new IllegalStateException("Can't get keychar from mouse event"); } public int getKeyLocation() { if (isKeyEvent()) { KeyEvent e = (KeyEvent) inputEvent; return e.getKeyLocation(); } throw new IllegalStateException("Can't get keylocation from mouse event"); } public boolean isActionKey() { if (isKeyEvent()) { KeyEvent e = (KeyEvent) inputEvent; return e.isActionKey(); } throw new IllegalStateException("Can't get isActionKey from mouse event"); } public int getModifiers() { if (!isFocusEvent()) { return inputEvent.getModifiers(); } throw new IllegalStateException("Can't get modifiers from focus event"); } public int getModifiersEx() { if (!isFocusEvent()) { return inputEvent.getModifiersEx(); } throw new IllegalStateException("Can't get modifiers ex from focus event"); } public int getClickCount() { if (isMouseEvent()) { return ((MouseEvent)inputEvent).getClickCount(); } throw new IllegalStateException("Can't get clickcount from key event"); } public long getWhen() { if (!isFocusEvent()) { return inputEvent.getWhen(); } throw new IllegalStateException("Can't get when from focus event"); } public boolean isAltDown() { if (!isFocusEvent()) { return inputEvent.isAltDown(); } throw new IllegalStateException("Can't get altdown from focus event"); } public boolean isControlDown() { if (!isFocusEvent()) { return inputEvent.isControlDown(); } throw new IllegalStateException("Can't get controldown from focus event"); } public boolean isMetaDown() { if (!isFocusEvent()) { return inputEvent.isMetaDown(); } throw new IllegalStateException("Can't get modifiers from focus event"); } public boolean isShiftDown() { if (!isFocusEvent()) { return inputEvent.isShiftDown(); } throw new IllegalStateException("Can't get shiftdown from focus event"); } public boolean isLeftMouseButton() { if (isMouseEvent()) { return SwingUtilities.isLeftMouseButton((MouseEvent)getSourceSwingEvent()); } throw new IllegalStateException("Can't get isLeftMouseButton from focus event"); } public boolean isMiddleMouseButton() { if (isMouseEvent()) { return SwingUtilities.isMiddleMouseButton((MouseEvent)getSourceSwingEvent()); } throw new IllegalStateException("Can't get isMiddleMouseButton from focus event"); } public boolean isRightMouseButton() { if (isMouseEvent()) { return SwingUtilities.isRightMouseButton((MouseEvent)getSourceSwingEvent()); } throw new IllegalStateException("Can't get isRightMouseButton from focus event"); } /** * Return true if another event handler has already handled this event. Event handlers should use * this as a hint before handling the event themselves and possibly reject events that have * already been handled. */ public boolean isHandled() { return handled; } /** * Set that this event has been handled by an event handler. This is a relaxed for of consuming events. * The event will continue to get dispatched to event handlers even after it is marked as handled, but * other event handlers that might conflict are expected to ignore events that have already been handled. */ public void setHandled(boolean handled) { this.handled = handled; } public int getButton() { if (isMouseEvent()) { return ((MouseEvent)inputEvent).getButton(); } throw new IllegalStateException("Can't get button from key event"); } public int getWheelRotation() { if (isMouseWheelEvent()) { return ((MouseWheelEvent) inputEvent).getWheelRotation(); } throw new IllegalStateException("Can't get wheel rotation from non-wheel event"); } public InputEvent getSourceSwingEvent() { return inputEvent; } //**************************************************************** // Classification - Methods to distinguish between mouse and key // events. //**************************************************************** public boolean isKeyEvent() { return inputEvent instanceof KeyEvent; } public boolean isMouseEvent() { return inputEvent instanceof MouseEvent; } public boolean isMouseWheelEvent() { return inputEvent instanceof MouseWheelEvent; } public boolean isFocusEvent() { return inputEvent == null; } public boolean isMouseEnteredOrMouseExited() { if (isMouseEvent()) { return inputEvent.getID() == MouseEvent.MOUSE_ENTERED || inputEvent.getID() == MouseEvent.MOUSE_EXITED; } return false; } /** * Returns whether or not this event is a popup menu trigger event for the * platform. Must not be called if this event isn't a mouse event. * <p><b>Note</b>: Popup menus are triggered differently on different * systems. Therefore, <code>isPopupTrigger</code> should be checked in both * <code>mousePressed</code> and <code>mouseReleased</code> for proper * cross-platform functionality. * * @return boolean, true if this event triggers a popup menu for this * platform * @throws IllegalStateException if this event is not a mouse event */ public boolean isPopupTrigger() { if (isMouseEvent()) { return ((MouseEvent) inputEvent).isPopupTrigger(); } throw new IllegalStateException("Can't get clickcount from key event"); } //**************************************************************** // Coordinate Systems - Methods for getting mouse location data // These methods are only designed for use with PInputEvents that // return true to the isMouseEvent method. //**************************************************************** /** * Return the mouse position in PCanvas coordinates. */ public Point2D getCanvasPosition() { return (Point2D) inputManager.getCurrentCanvasPosition().clone(); } /** * Return the delta between the last and current mouse * position in PCanvas coordinates. */ public PDimension getCanvasDelta() { Point2D last = inputManager.getLastCanvasPosition(); Point2D current = inputManager.getCurrentCanvasPosition(); return new PDimension(current.getX() - last.getX(), current.getY() - last.getY()); } /** * Return the mouse position relative to a given node on the pick path. */ public Point2D getPositionRelativeTo(PNode nodeOnPath) { Point2D r = getCanvasPosition(); return pickPath.canvasToLocal(r, nodeOnPath); } /** * Return the delta between the last and current mouse positions * relative to a given node on the pick path. */ public PDimension getDeltaRelativeTo(PNode nodeOnPath) { PDimension r = getCanvasDelta(); return (PDimension) pickPath.canvasToLocal(r, nodeOnPath); } /** * Return the mouse position transformed through the view transform of * the bottom camera. */ public Point2D getPosition() { Point2D r = getCanvasPosition(); pickPath.canvasToLocal(r, getCamera()); return getCamera().localToView(r); } /** * Return the delta between the last and current mouse positions * transformed through the view transform of the bottom camera. */ public PDimension getDelta() { PDimension r = getCanvasDelta(); pickPath.canvasToLocal(r, getCamera()); return (PDimension) getCamera().localToView(r); } //**************************************************************** // Debugging - methods for debugging //**************************************************************** /** * Returns a string representation of this object for debugging purposes. */ public String toString() { StringBuffer result = new StringBuffer(); result.append(super.toString().replaceAll(".*\\.", "")); result.append('['); if (handled) { result.append("handled"); } result.append(']'); return result.toString(); } }