/*************************************************** * * cismet GmbH, Saarbruecken, Germany * * ... and it just works. * ****************************************************/ /** * Copyright (C) 1998-2000 by University of Maryland, College Park, MD 20742, USA * All rights reserved. */ package pswing; import edu.umd.cs.piccolo.PCamera; import edu.umd.cs.piccolo.PNode; import edu.umd.cs.piccolo.event.PInputEvent; import edu.umd.cs.piccolo.event.PInputEventListener; import java.awt.*; import java.awt.event.InputEvent; import java.awt.event.MouseEvent; import java.awt.geom.NoninvertibleTransformException; import java.awt.geom.Point2D; import javax.swing.*; /** * Event handler to send MousePressed, MouseReleased, MouseMoved, MouseClicked, and MouseDragged events on Swing * components within a ZCanvas. * * @author Ben Bederson * @author Lance Good * @version $Revision$, $Date$ */ public class PSwingEventHandler implements PInputEventListener { //~ Instance fields -------------------------------------------------------- /** The node to listen to for events. */ protected PNode listenNode = null; /** True when event handlers are set active. */ protected boolean active = false; // The previous component - used to generate mouseEntered and // mouseExited events Component prevComponent = null; // The components whose cursor is on the screen Component cursorComponent = null; // Previous points used in generating mouseEntered and mouseExited // events Point2D prevPoint = null; Point2D prevOff = null; // The focused ZSwing for the left button PSwing focusPSwingLeft = null; // The focused node for the left button PNode focusNodeLeft = null; // The focused component for the left button Component focusComponentLeft = null; // Offsets for the focused node for the left button int focusOffXLeft = 0; int focusOffYLeft = 0; // The focused ZSwing for the middle button PSwing focusPSwingMiddle = null; // The focused node for the middle button PNode focusNodeMiddle = null; // The focused component for the middle button Component focusComponentMiddle = null; // Offsets for the focused node for the middle button int focusOffXMiddle = 0; int focusOffYMiddle = 0; // The focused ZSwing for the right button PSwing focusPSwingRight = null; // The focused node for the right button PNode focusNodeRight = null; // The focused component for the right button Component focusComponentRight = null; // Offsets for the focused node for the right button int focusOffXRight = 0; int focusOffYRight = 0; // The canvas PSwingCanvas canvas; private boolean recursing = false; //~ Constructors ----------------------------------------------------------- /** * Constructs a new ZSwingEventHandler for the given canvas. * * @param canvas DOCUMENT ME! */ public PSwingEventHandler(final PSwingCanvas canvas) { this.canvas = canvas; } // Constructor that adds the mouse listeners to a /** * Constructs a new ZSwingEventHandler for the given canvas, and a node that will recieve the mouse events. * * @param canvas the canvas associated with this ZSwingEventHandler. * @param node the node the mouse listeners will be attached to. */ public PSwingEventHandler(final PSwingCanvas canvas, final PNode node) { this.canvas = canvas; listenNode = node; } //~ Methods ---------------------------------------------------------------- /** * DOCUMENT ME! * * @param active DOCUMENT ME! */ public void setActive(final boolean active) { if (this.active && !active) { if (listenNode != null) { this.active = false; listenNode.removeInputEventListener(this); } } else if (!this.active && active) { if (listenNode != null) { this.active = true; listenNode.addInputEventListener(this); } } } /** * Determines if this event handler is active. * * @return True if active */ public boolean isActive() { return active; } /** * A re-implementation of Container.findComponentAt that ensures that the returned component is *SHOWING* not just * visible. * * @param c DOCUMENT ME! * @param x DOCUMENT ME! * @param y DOCUMENT ME! * * @return DOCUMENT ME! */ Component findComponentAt(final Component c, final int x, final int y) { if (!c.contains(x, y)) { return null; } if (c instanceof Container) { final Container contain = ((Container)c); final int ncomponents = contain.getComponentCount(); final Component[] component = contain.getComponents(); for (int i = 0; i < ncomponents; i++) { Component comp = component[i]; if (comp != null) { final Point p = comp.getLocation(); if (comp instanceof Container) { comp = findComponentAt(comp, x - (int)p.getX(), y - (int)p.getY()); } else { comp = comp.getComponentAt(x - (int)p.getX(), y - (int)p.getY()); } if ((comp != null) && comp.isShowing()) { return comp; } } } } return c; } /** * Determines if any Swing components being used in Jazz should receive the given MouseEvent and forwards the event * to that component. However, mouseEntered and mouseExited are independent of the buttons Also, notice the notes on * mouseEntered and mouseExited * * @param e1 DOCUMENT ME! * @param aEvent DOCUMENT ME! */ void dispatchEvent(final PSwingMouseEvent e1, final PInputEvent aEvent) { PNode grabNode = null; Component comp = null; Point2D pt = null; final PNode currentNode = e1.getPath().getPickedNode(); // The offsets to put the event in the correct context int offX = 0; int offY = 0; final PNode vc = e1.getCurrentNode(); final PNode visualNode = currentNode; if (vc instanceof PSwing) { final PSwing swing = (PSwing)vc; grabNode = visualNode; if (grabNode.isDescendentOf(canvas.getRoot())) { pt = new Point2D.Double(e1.getX(), e1.getY()); cameraToLocal(e1.getPath().getTopCamera(), pt, grabNode); prevPoint = (Point2D)pt.clone(); // This is only partially fixed to find the deepest // component at pt. It needs to do something like // package private method: // Container.getMouseEventTarget(int,int,boolean) comp = findComponentAt(swing.getComponent(), (int)pt.getX(), (int)pt.getY()); // We found the right component - but we need to // get the offset to put the event in the component's // coordinates if ((comp != null) && (comp != swing.getComponent())) { for (Component c = comp; c != swing.getComponent(); c = c.getParent()) { offX += c.getLocation().getX(); offY += c.getLocation().getY(); } } // Mouse Pressed gives focus - effects Mouse Drags and // Mouse Releases if ((comp != null) && (e1.getID() == MouseEvent.MOUSE_PRESSED)) { if (SwingUtilities.isLeftMouseButton(e1)) { focusPSwingLeft = swing; focusComponentLeft = comp; focusNodeLeft = visualNode; focusOffXLeft = offX; focusOffYLeft = offY; } else if (SwingUtilities.isMiddleMouseButton(e1)) { focusPSwingMiddle = swing; focusComponentMiddle = comp; focusNodeMiddle = visualNode; focusOffXMiddle = offX; focusOffYMiddle = offY; } else if (SwingUtilities.isRightMouseButton(e1)) { focusPSwingRight = swing; focusComponentRight = comp; focusNodeRight = visualNode; focusOffXRight = offX; focusOffYRight = offY; } } } // This first case we don't want to give events to just // any Swing component - but to the one that got the // original mousePressed if ((e1.getID() == MouseEvent.MOUSE_DRAGGED) || (e1.getID() == MouseEvent.MOUSE_RELEASED)) { // LEFT MOUSE BUTTON if (SwingUtilities.isLeftMouseButton(e1) && (focusComponentLeft != null)) { if (focusNodeLeft.isDescendentOf(canvas.getRoot())) { pt = new Point2D.Double(e1.getX(), e1.getY()); cameraToLocal(e1.getPath().getTopCamera(), pt, focusNodeLeft); final MouseEvent e_temp = new MouseEvent( focusComponentLeft, e1.getID(), e1.getWhen(), e1.getModifiers(), (int)pt.getX() - focusOffXLeft, (int)pt.getY() - focusOffYLeft, e1.getXOnScreen(), e1.getYOnScreen(), e1.getClickCount(), e1.isPopupTrigger(), e1.getButton()); final PSwingMouseEvent e2 = PSwingMouseEvent.createMouseEvent(e_temp.getID(), e_temp, aEvent); if ((pt.getX() > 0) && (pt.getY() > 0) && (pt.getX() < focusPSwingLeft.getWidth()) && (pt.getY() < focusPSwingLeft.getHeight())) { dispatchEvent(focusComponentLeft, e2); } else { dispatchEvent(focusComponentLeft, e1); } } else { dispatchEvent(focusComponentLeft, e1); } focusPSwingLeft.repaint(); e1.consume(); if (e1.getID() == MouseEvent.MOUSE_RELEASED) { focusComponentLeft = null; focusNodeLeft = null; } } // MIDDLE MOUSE BUTTON if (SwingUtilities.isMiddleMouseButton(e1) && (focusComponentMiddle != null)) { if (focusNodeMiddle.isDescendentOf(canvas.getRoot())) { pt = new Point2D.Double(e1.getX(), e1.getY()); cameraToLocal(e1.getPath().getTopCamera(), pt, focusNodeMiddle); final MouseEvent e_temp = new MouseEvent( focusComponentMiddle, e1.getID(), e1.getWhen(), e1.getModifiers(), (int)pt.getX() - focusOffXMiddle, (int)pt.getY() - focusOffYMiddle, e1.getClickCount(), e1.isPopupTrigger()); final PSwingMouseEvent e2 = PSwingMouseEvent.createMouseEvent(e_temp.getID(), e_temp, aEvent); dispatchEvent(focusComponentMiddle, e2); } else { dispatchEvent(focusComponentMiddle, e1); } focusPSwingMiddle.repaint(); e1.consume(); if (e1.getID() == MouseEvent.MOUSE_RELEASED) { focusComponentMiddle = null; focusNodeMiddle = null; } } // RIGHT MOUSE BUTTON if (SwingUtilities.isRightMouseButton(e1) && (focusComponentRight != null)) { if (focusNodeRight.isDescendentOf(canvas.getRoot())) { pt = new Point2D.Double(e1.getX(), e1.getY()); cameraToLocal(e1.getPath().getTopCamera(), pt, focusNodeRight); final MouseEvent e_temp = new MouseEvent( focusComponentRight, e1.getID(), e1.getWhen(), e1.getModifiers(), (int)pt.getX() - focusOffXRight, (int)pt.getY() - focusOffYRight, e1.getClickCount(), e1.isPopupTrigger()); final PSwingMouseEvent e2 = PSwingMouseEvent.createMouseEvent(e_temp.getID(), e_temp, aEvent); dispatchEvent(focusComponentRight, e2); } else { dispatchEvent(focusComponentRight, e1); } focusPSwingRight.repaint(); e1.consume(); if (e1.getID() == MouseEvent.MOUSE_RELEASED) { focusComponentRight = null; focusNodeRight = null; } } } // This case covers the cases mousePressed, mouseClicked, // and mouseMoved events else if (((e1.getID() == MouseEvent.MOUSE_PRESSED) || (e1.getID() == MouseEvent.MOUSE_CLICKED) || (e1.getID() == MouseEvent.MOUSE_MOVED)) && (comp != null)) { final MouseEvent e_temp = new MouseEvent( comp, e1.getID(), e1.getWhen(), e1.getModifiers(), (int)pt.getX() - offX, (int)pt.getY() - offY, e1.getClickCount(), e1.isPopupTrigger()); final PSwingMouseEvent e2 = PSwingMouseEvent.createMouseEvent(e_temp.getID(), e_temp, aEvent); dispatchEvent(comp, e2); e1.consume(); } // Now we need to check if an exit or enter event needs to // be dispatched - this code is independent of the mouseButtons. // I tested in normal Swing to see the correct behavior. if (prevComponent != null) { // This means mouseExited // This shouldn't happen - since we're only getting node events if ((comp == null) || (e1.getID() == MouseEvent.MOUSE_EXITED)) { final MouseEvent e_temp = new MouseEvent( prevComponent, MouseEvent.MOUSE_EXITED, e1.getWhen(), 0, (int)prevPoint.getX() - (int)prevOff.getX(), (int)prevPoint.getY() - (int)prevOff.getY(), e1.getClickCount(), e1.isPopupTrigger()); final PSwingMouseEvent e2 = PSwingMouseEvent.createMouseEvent(e_temp.getID(), e_temp, aEvent); dispatchEvent(prevComponent, e2); prevComponent = null; if (e1.getID() == MouseEvent.MOUSE_EXITED) { e1.consume(); } } // This means mouseExited prevComponent and mouseEntered comp else if (prevComponent != comp) { MouseEvent e_temp = new MouseEvent( prevComponent, MouseEvent.MOUSE_EXITED, e1.getWhen(), 0, (int)prevPoint.getX() - (int)prevOff.getX(), (int)prevPoint.getY() - (int)prevOff.getY(), e1.getClickCount(), e1.isPopupTrigger()); PSwingMouseEvent e2 = PSwingMouseEvent.createMouseEvent(e_temp.getID(), e_temp, aEvent); dispatchEvent(prevComponent, e2); e_temp = new MouseEvent( comp, MouseEvent.MOUSE_ENTERED, e1.getWhen(), 0, (int)prevPoint.getX() - offX, (int)prevPoint.getY() - offY, e1.getClickCount(), e1.isPopupTrigger()); e2 = PSwingMouseEvent.createMouseEvent(e_temp.getID(), e_temp, aEvent); comp.dispatchEvent(e2); } } else { // This means mouseEntered if (comp != null) { final MouseEvent e_temp = new MouseEvent( comp, MouseEvent.MOUSE_ENTERED, e1.getWhen(), 0, (int)prevPoint.getX() - offX, (int)prevPoint.getY() - offY, e1.getClickCount(), e1.isPopupTrigger()); final PSwingMouseEvent e2 = PSwingMouseEvent.createMouseEvent(e_temp.getID(), e_temp, aEvent); dispatchEvent(comp, e2); } } // Set the previous variables for next time prevComponent = comp; if (comp != null) { prevOff = new Point2D.Double(offX, offY); } } } /** * DOCUMENT ME! * * @param target DOCUMENT ME! * @param event DOCUMENT ME! */ private void dispatchEvent(final Component target, final PSwingMouseEvent event) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { target.dispatchEvent(event); } }); } /** * DOCUMENT ME! * * @param topCamera DOCUMENT ME! * @param pt DOCUMENT ME! * @param node DOCUMENT ME! */ private void cameraToLocal(final PCamera topCamera, final Point2D pt, final PNode node) { try { topCamera.getViewTransform().inverseTransform(pt, pt); } catch (NoninvertibleTransformException e) { e.printStackTrace(); } if (node != null) { node.globalToLocal(pt); } } @Override public void processEvent(final PInputEvent aEvent, final int type) { if (aEvent.isMouseEvent()) { final InputEvent sourceSwingEvent = aEvent.getSourceSwingEvent(); final PSwingMouseEvent pSwingMouseEvent = PSwingMouseEvent.createMouseEvent(sourceSwingEvent.getID(), (MouseEvent)sourceSwingEvent, aEvent); if (!recursing) { recursing = true; dispatchEvent(pSwingMouseEvent, aEvent); recursing = false; } } } }