/* * Copyright (c) 2008-2011, Piccolo2D project, http://piccolo2d.org * Copyright (c) 1998-2008, 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. * * None of the name of the University of Maryland, the name of the Piccolo2D project, or 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. */ 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 { /** The underlying Swing Event. */ private final InputEvent inputEvent; /** Path relating to the current mouse event. */ private PPickPath pickPath; /** Input manager responsible for the creation of this event. */ private final PInputManager inputManager; /** Flag used to identify this event as handled. */ private boolean handled; /** * Create an event with the given inputManager and based on the given swing * event. * * @param inputManager source of PInputEvent * @param event underlying swing event */ public PInputEvent(final PInputManager inputManager, final InputEvent event) { inputEvent = event; this.inputManager = inputManager; } /** * Changes the cursor to the one provided and stores it on the cursor stack * for later retrieval. * * @param cursor cursor to push on cursor stack */ public void pushCursor(final Cursor cursor) { final PComponent component = getTopCamera().getComponent(); component.pushCursor(cursor); } /** * Removes the top most cursor from the cursor stack and sets it as the * current cursor. */ public void popCursor() { final 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. * * @return the current PickPath's bottom camera. */ public PCamera getCamera() { return getPath().getBottomCamera(); } /** * Return the topmost camera this is painting. This is the camera associated * with the PCanvas that requested the current repaint. * * @return topmost camera on the pick path */ 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. * * @return component attached to the top camera of the current pick path */ 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. * * @return input manager that dispatched this event */ public PInputManager getInputManager() { return inputManager; } /** * Return the PPickPath associated with this input event. * * @return pick path associated with this event (may be null) */ public PPickPath getPath() { return pickPath; } /** * Sets the PIckPath associated with this mouse event. * * @param path path to associate with this mouse event */ public void setPath(final PPickPath path) { pickPath = path; } /** * Return the bottom node on the current pickpath, that is the picked node * furthest from the root node. * * @return the currently picked node of this mouse event */ public PNode getPickedNode() { if (pickPath == null) { return null; } return pickPath.getPickedNode(); } // **************************************************************** // Basics // **************************************************************** /** * Returns the key code associated with a key event. * * @return key code associated with a key event */ public int getKeyCode() { if (isKeyEvent()) { final KeyEvent e = (KeyEvent) inputEvent; return e.getKeyCode(); } throw new IllegalStateException("Can't get keycode from mouse event"); } /** * Returns the character associated with a key event. * * @return char associated with a key event */ public char getKeyChar() { if (isKeyEvent()) { final KeyEvent e = (KeyEvent) inputEvent; return e.getKeyChar(); } throw new IllegalStateException("Can't get keychar from mouse event"); } /** * Returns the location on the keyboard from which the key stroke * originated. * * @return location on keyboard from which stroke originated. */ public int getKeyLocation() { if (isKeyEvent()) { final KeyEvent e = (KeyEvent) inputEvent; return e.getKeyLocation(); } throw new IllegalStateException("Can't get keylocation from mouse event"); } /** * Returns whether the key event involves the action key. * * @return true if key involved is the action key */ public boolean isActionKey() { if (isKeyEvent()) { final KeyEvent e = (KeyEvent) inputEvent; return e.isActionKey(); } throw new IllegalStateException("Can't get isActionKey from mouse event"); } /** * Returns the modifiers provided for the input event by swing. * * @return modifier flags for the input event */ public int getModifiers() { if (!isFocusEvent()) { return inputEvent.getModifiers(); } throw new IllegalStateException("Can't get modifiers from focus event"); } /** * Returns the extended modifiers provided for the input event by swing. * * @return extended modifies of input event */ public int getModifiersEx() { if (!isFocusEvent()) { return inputEvent.getModifiersEx(); } throw new IllegalStateException("Can't get modifiers ex from focus event"); } /** * Returns the click count of the mouse event. * * @return click count of mouse event */ public int getClickCount() { if (isMouseEvent()) { return ((MouseEvent) inputEvent).getClickCount(); } throw new IllegalStateException("Can't get clickcount from key event"); } /** * Returns the time at which the event was emitted. * * @return time at which the vent was emitted */ public long getWhen() { if (!isFocusEvent()) { return inputEvent.getWhen(); } throw new IllegalStateException("Can't get when from focus event"); } /** * Returns whether the alt key is currently down. * * @return true if alt key is down */ public boolean isAltDown() { if (!isFocusEvent()) { return inputEvent.isAltDown(); } throw new IllegalStateException("Can't get altdown from focus event"); } /** * Returns whether the control key is currently down. * * @return true if control key is down */ public boolean isControlDown() { if (!isFocusEvent()) { return inputEvent.isControlDown(); } throw new IllegalStateException("Can't get controldown from focus event"); } /** * Returns whether the meta key is currently down. * * @return true if meta key is down */ public boolean isMetaDown() { if (!isFocusEvent()) { return inputEvent.isMetaDown(); } throw new IllegalStateException("Can't get modifiers from focus event"); } /** * Returns whether the shift key is currently down. * * @return true if shift key is down */ public boolean isShiftDown() { if (!isFocusEvent()) { return inputEvent.isShiftDown(); } throw new IllegalStateException("Can't get shiftdown from focus event"); } /** * Returns whether the mouse event involves the left mouse button. * * @return true if left mouse button is involved the mouse event */ public boolean isLeftMouseButton() { if (isMouseEvent()) { return SwingUtilities.isLeftMouseButton((MouseEvent) getSourceSwingEvent()); } throw new IllegalStateException("Can't get isLeftMouseButton from focus event"); } /** * Returns whether the mouse event involves the middle mouse button. * * @return true if middle mouse button is involved the mouse event */ public boolean isMiddleMouseButton() { if (isMouseEvent()) { return SwingUtilities.isMiddleMouseButton((MouseEvent) getSourceSwingEvent()); } throw new IllegalStateException("Can't get isMiddleMouseButton from focus event"); } /** * Returns whether the mouse event involves the right mouse button. * * @return true if right mouse button is involved the mouse 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. * * @return true if event has been marked as 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. * * @param handled whether the event is marked */ public void setHandled(final boolean handled) { this.handled = handled; } /** * Returns the mouse button value of the underlying mouse event. * * @return button value of underlying mouse event */ public int getButton() { if (isMouseEvent()) { return ((MouseEvent) inputEvent).getButton(); } throw new IllegalStateException("Can't get button from key event"); } /** * Returns the current value of the wheel rotation on Mouse Wheel Rotation * events. * * @return wheel rotation value */ public int getWheelRotation() { if (isMouseWheelEvent()) { return ((MouseWheelEvent) inputEvent).getWheelRotation(); } throw new IllegalStateException("Can't get wheel rotation from non-wheel event"); } /** * Returns the underlying swing event that this PInputEvent is wrapping. * * @return underlying swing event */ public InputEvent getSourceSwingEvent() { return inputEvent; } // **************************************************************** // Classification - Methods to distinguish between mouse and key // events. // **************************************************************** /** * Returns whether the underlying event is a KeyEvent. * * @return true if is key event */ public boolean isKeyEvent() { return inputEvent instanceof KeyEvent; } /** * Returns whether the underlying event is a MouseEvent. * * @return true if is mouse event */ public boolean isMouseEvent() { return inputEvent instanceof MouseEvent; } /** * Returns whether the underlying event is a Mouse Wheel Event. * * @return true if is a mouse wheel event */ public boolean isMouseWheelEvent() { return inputEvent instanceof MouseWheelEvent; } /** * Returns whether the underlying event is a Focus Event. * * @return true if is focus event */ public boolean isFocusEvent() { return inputEvent == null; } /** * Returns whether the underlying event is a mouse entered or exited event. * * @return true if is a mouse entered or exited event */ 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 */ 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. * * @return 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. * * @return delta between last and current mouse position as measured by the * PCanvas */ public PDimension getCanvasDelta() { final Point2D last = inputManager.getLastCanvasPosition(); final 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. * * @param nodeOnPath node on the current PPickPath * * @return mouse position relative to the provided node on pick path */ public Point2D getPositionRelativeTo(final PNode nodeOnPath) { if (pickPath == null) { throw new RuntimeException("Attempting to use pickPath for a non-mouse event."); } final 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. * * @param nodeOnPath node from which to measure * @return delta between current mouse position and a given node on the pick * path */ public PDimension getDeltaRelativeTo(final PNode nodeOnPath) { if (pickPath == null) { throw new RuntimeException("Attempting to use pickPath for a non-mouse event."); } final PDimension r = getCanvasDelta(); return (PDimension) pickPath.canvasToLocal(r, nodeOnPath); } /** * Return the mouse position transformed through the view transform of the * bottom camera. * * @return mouse position as measured by the bottom camera */ public Point2D getPosition() { if (pickPath == null) { throw new RuntimeException("Attempting to use pickPath for a non-mouse event."); } final 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. * * @return delta between last and current mouse position as measured by the * bottom camera */ public PDimension getDelta() { if (pickPath == null) { throw new RuntimeException("Attempting to use pickPath for a non-mouse event."); } final PDimension r = getCanvasDelta(); pickPath.canvasToLocal(r, getCamera()); return (PDimension) getCamera().localToView(r); } /** * Returns a string representation of this object for debugging purposes. * * @return string representation of this object */ public String toString() { final StringBuffer result = new StringBuffer(); result.append(super.toString().replaceAll(".*\\.", "")); result.append('['); if (handled) { result.append("handled"); } result.append(']'); return result.toString(); } /** * Modified by miura * @param satn */ private String actionCommand; /** * Modified by miura * @param s */ public void setActionCommand(String s){ actionCommand = s; } /** * Modified by miura * @param satn */ public String getActionCommand(){ return actionCommand; } }