diff --git a/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwing.java b/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwing.java index 5088c45..bac488a 100644 --- a/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwing.java +++ b/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwing.java @@ -220,11 +220,18 @@ /** Minimum font size. */ private double minFontSize = Double.MAX_VALUE; - /** Default stroke, new BasicStroke(). Cannot be made static because BasicStroke is not serializable. */ + /** + * Default stroke, new BasicStroke(). Cannot be made static + * because BasicStroke is not serializable. + */ private transient Stroke defaultStroke = new BasicStroke(); - /** Default font, 12 point "SansSerif". Will be made final in version 2.0. */ - // public static final Font DEFAULT_FONT = new Font(Font.SANS_SERIF, Font.PLAIN, 12); jdk 1.6+ + /** + * Default font, 12 point "SansSerif". Will be made final in + * version 2.0. + */ + // public static final Font DEFAULT_FONT = new Font(Font.SANS_SERIF, + // Font.PLAIN, 12); jdk 1.6+ public static final Font DEFAULT_FONT = new Font("SansSerif", Font.PLAIN, 12); /** Greek threshold in scale. */ @@ -294,7 +301,6 @@ this(component); } - /** * Ensures the bounds of the underlying component are accurate, and sets the * bounds of this PNode. @@ -347,8 +353,9 @@ } /** - * Workaround to prevent text-rendering Swing components from drawing an ellipsis incorrectly. - * + * Workaround to prevent text-rendering Swing components from drawing an + * ellipsis incorrectly. + * * @param text text * @param icon icon * @param iconGap icon gap @@ -407,8 +414,7 @@ * specified paint context */ protected boolean shouldRenderGreek(final PPaintContext paintContext) { - return paintContext.getScale() < greekThreshold - || minFontSize * paintContext.getScale() < 0.5; + return paintContext.getScale() < greekThreshold || minFontSize * paintContext.getScale() < 0.5; } /** @@ -419,7 +425,7 @@ * * @param paintContext paint context */ - protected void paintGreek(PPaintContext paintContext) { + protected void paintGreek(final PPaintContext paintContext) { final Graphics2D graphics = paintContext.getGraphics(); final Color background = component.getBackground(); final Color foreground = component.getForeground(); @@ -448,7 +454,7 @@ /** * Paint the Swing component with the specified paint context. - * + * * @param paintContext paint context */ protected void paintComponent(final PPaintContext paintContext) { diff --git a/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingCanvas.java b/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingCanvas.java index ef93621..2d68151 100644 --- a/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingCanvas.java +++ b/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingCanvas.java @@ -59,8 +59,8 @@ } private void initRepaintManager() { - final RepaintManager repaintManager = RepaintManager.currentManager(this); - if (!(repaintManager instanceof PSwingRepaintManager)) { + final RepaintManager repaintManager = RepaintManager.currentManager(this); + if (!(repaintManager instanceof PSwingRepaintManager)) { RepaintManager.setCurrentManager(new PSwingRepaintManager()); } } diff --git a/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingEvent.java b/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingEvent.java new file mode 100644 index 0000000..f63af63 --- /dev/null +++ b/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingEvent.java @@ -0,0 +1,127 @@ +/* + * Copyright 2007 Stephen Chin + * + * All rights reserved. + */ +package edu.umd.cs.piccolox.pswing; + +import java.awt.event.MouseEvent; +import java.awt.geom.Point2D; + +import edu.umd.cs.piccolo.PNode; +import edu.umd.cs.piccolo.util.PPickPath; + +public interface PSwingEvent { + /** + * Returns the x,y position of the event in the local coordinate system of + * the node the event occurred on. + * + * @return a Point2D object containing the x and y coordinates local to the + * node. + */ + Point2D getLocalPoint(); + + /** + * Returns the horizontal x position of the event in the local coordinate + * system of the node the event occurred on. + * + * @return x a double indicating horizontal position local to the node. + */ + double getLocalX(); + + /** + * Returns the vertical y position of the event in the local coordinate + * system of the node the event occurred on. + * + * @return y a double indicating vertical position local to the node. + */ + double getLocalY(); + + /** + * Determine the event type. + * + * @return the id + */ + int getID(); + + /** + * Determine the node the event originated at. If an event percolates up the + * tree and is handled by an event listener higher up in the tree than the + * original node that generated the event, this returns the original node. + * For mouse drag and release events, this is the node that the original + * matching press event went to - in other words, the event is 'grabbed' by + * the originating node. + * + * @return the node + */ + PNode getNode(); + + /** + * Determine the path the event took from the PCanvas down to the visual + * component. + * + * @return the path + */ + PPickPath getPath(); + + /** + * Determine the node the event originated at. If an event percolates up the + * tree and is handled by an event listener higher up in the tree than the + * original node that generated the event, this returns the original node. + * For mouse drag and release events, this is the node that the original + * matching press event went to - in other words, the event is 'grabbed' by + * the originating node. + * + * @return the node + */ + PNode getGrabNode(); + + /** + * Return the path from the PCanvas down to the currently grabbed object. + * + * @return the path + */ + PPickPath getGrabPath(); + + /** + * Get the current node that is under the cursor. This may return a + * different result then getGrabNode() when in a MOUSE_RELEASED or + * MOUSE_DRAGGED event. + * + * @return the current node. + */ + PNode getCurrentNode(); + + /** + * Get the path from the PCanvas down to the visual component currently + * under the mouse.This may give a different result then getGrabPath() + * during a MOUSE_DRAGGED or MOUSE_RELEASED operation. + * + * @return the current path. + */ + PPickPath getCurrentPath(); + + /** + * Calls appropriate method on the listener based on this events ID. + * + * @param listener the MouseListener or MouseMotionListener to dispatch to. + */ + void dispatchTo(Object listener); + + /** + * Set the souce of this event. As the event is fired up the tree the source + * of the event will keep changing to reflect the scenegraph object that is + * firing the event. + * + * @param aSource + */ + void setSource(Object aSource); + + /** + * Returns this event as a mouse event. This reduces the need to cast + * instances of this interface when they are known to all extend MouseEvent. + * + * @return this object casted to a MouseEvent + */ + MouseEvent asMouseEvent(); +} \ No newline at end of file diff --git a/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingEventHandler.java b/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingEventHandler.java index 5706b57..8c56920 100644 --- a/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingEventHandler.java +++ b/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingEventHandler.java @@ -33,6 +33,7 @@ import java.awt.Point; import java.awt.event.InputEvent; import java.awt.event.MouseEvent; +import java.awt.event.MouseWheelEvent; import java.awt.geom.AffineTransform; import java.awt.geom.NoninvertibleTransformException; import java.awt.geom.Point2D; @@ -44,6 +45,7 @@ import edu.umd.cs.piccolo.PNode; import edu.umd.cs.piccolo.event.PInputEvent; import edu.umd.cs.piccolo.event.PInputEventListener; +import edu.umd.cs.piccolo.util.PAffineTransform; import edu.umd.cs.piccolo.util.PAffineTransformException; /** @@ -57,34 +59,46 @@ public class PSwingEventHandler implements PInputEventListener { private PNode listenNode = null; // used to listen to for events - private boolean active = false; // True when event handlers are set active. - // The previous component - used to generate mouseEntered and - // mouseExited events - private Component prevComponent = null; + /** Tracks whether this event handler is active. */ + private boolean active = false; - // Previous points used in generating mouseEntered and mouseExited events + /** + * The previous component - used to generate mouseEntered and mouseExited + * events. + */ + private Component previousComponent = null; + + /** Previous point used for mouseEntered and exited events. */ private Point2D prevPoint = null; - private Point2D prevOff = null; + + /** Previous offset used for mouseEntered and exited events. */ + private Point2D previousOffset = null; private boolean recursing = false;// to avoid accidental recursive handling + /** Used for tracking the left button's state. */ private final ButtonData leftButtonData = new ButtonData(); - private final ButtonData rightButtonData = new ButtonData(); + + /** Used for tracking the middle button's state. */ private final ButtonData middleButtonData = new ButtonData(); + /** Used for tracking the right button's state. */ + private final ButtonData rightButtonData = new ButtonData(); + + /** The Canvas in which all this pswing activity is taking place. */ private final PSwingCanvas canvas; /** * Constructs a new PSwingEventHandler for the given canvas, and a node that - * will recieve the mouse events. + * will receive the mouse events. * * @param canvas the canvas associated with this PSwingEventHandler. * @param node the node the mouse listeners will be attached to. */ - public PSwingEventHandler(final PSwingCanvas canvas, final PNode node) { + public PSwingEventHandler(final PSwingCanvas canvas, final PNode listenNode) { this.canvas = canvas; - listenNode = node; + this.listenNode = listenNode; } /** @@ -113,7 +127,7 @@ } /** - * Determines if this event handler is active. + * Returns if this event handler is active. * * @return True if active */ @@ -124,38 +138,45 @@ /** * Finds the component at the specified location (must be showing). * - * @param c + * @param component * @param x * @param y * @return the component at the specified location. */ - private Component findShowingComponentAt(final Component c, final int x, final int y) { - if (!c.contains(x, y)) { + private Component findShowingComponentAt(final Component component, final int x, final int y) { + if (!component.contains(x, y)) { return null; } - if (c instanceof Container) { - final Container contain = (Container) c; - final int ncomponents = contain.getComponentCount(); - final Component component[] = contain.getComponents(); + if (component instanceof Container) { + final Container contain = (Container) component; + final Component child = findShowingChildAt(contain, x, y); + if (child != null) { + return child; + } + } + return component; + } - for (int i = 0; i < ncomponents; i++) { - Component comp = component[i]; - if (comp != null) { - final Point p = comp.getLocation(); - if (comp instanceof Container) { - comp = findShowingComponentAt(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; - } + private Component findShowingChildAt(final Container container, final int x, final int y) { + final Component[] children = container.getComponents(); + + for (int i = 0; i < children.length; i++) { + Component child = children[i]; + if (child != null) { + final Point p = child.getLocation(); + if (child instanceof Container) { + child = findShowingComponentAt(child, x - p.x, y - p.y); + } + else { + child = child.getComponentAt(x - p.x, y - p.y); + } + if (child != null && child.isShowing()) { + return child; } } } - return c; + return null; } /** @@ -167,55 +188,49 @@ * @param pSwingMouseEvent * @param aEvent */ - void dispatchEvent(final PSwingMouseEvent pSwingMouseEvent, final PInputEvent aEvent) { - Component comp = null; - Point2D pt = null; + void dispatchEvent(final PSwingEvent pSwingMouseEvent, final PInputEvent aEvent) { + final MouseEvent mEvent = pSwingMouseEvent.asMouseEvent(); final PNode pickedNode = pSwingMouseEvent.getPath().getPickedNode(); - - // The offsets to put the event in the correct context - int offX = 0; - int offY = 0; - final PNode currentNode = pSwingMouseEvent.getCurrentNode(); - if (currentNode instanceof PSwing) { + Component comp = null; + Point point = null; + + Point offset = new Point(); + + if (currentNode instanceof PSwing && pickedNode.isDescendentOf(canvas.getRoot())) { final PSwing swing = (PSwing) currentNode; final PNode grabNode = pickedNode; - if (grabNode.isDescendentOf(canvas.getRoot())) { - pt = new Point2D.Double(pSwingMouseEvent.getX(), pSwingMouseEvent.getY()); - cameraToLocal(pSwingMouseEvent.getPath().getTopCamera(), pt, grabNode); - prevPoint = new Point2D.Double(pt.getX(), pt.getY()); + point = new Point(mEvent.getX(), mEvent.getY()); + cameraToLocal(pSwingMouseEvent.getPath().getTopCamera(), point, grabNode); + prevPoint = (Point) point.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 = findShowingComponentAt(swing.getComponent(), (int) pt.getX(), (int) pt.getY()); + // 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 = findShowingComponentAt(swing.getComponent(), point.x, point.y); - // 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(); - } + // 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()) { + offset = extractSwingOffset(comp, swing); + } + + // Mouse Pressed gives focus - effects Mouse Drags and + // Mouse Releases + if (comp != null && isMousePress(pSwingMouseEvent)) { + if (SwingUtilities.isLeftMouseButton(mEvent)) { + leftButtonData.setState(pickedNode, comp, offset.x, offset.y); } - - // Mouse Pressed gives focus - effects Mouse Drags and - // Mouse Releases - if (comp != null && pSwingMouseEvent.getID() == MouseEvent.MOUSE_PRESSED) { - if (SwingUtilities.isLeftMouseButton(pSwingMouseEvent)) { - leftButtonData.setState(swing, pickedNode, comp, offX, offY); - } - else if (SwingUtilities.isMiddleMouseButton(pSwingMouseEvent)) { - middleButtonData.setState(swing, pickedNode, comp, offX, offY); - } - else if (SwingUtilities.isRightMouseButton(pSwingMouseEvent)) { - rightButtonData.setState(swing, pickedNode, comp, offX, offY); - } + else if (SwingUtilities.isMiddleMouseButton(mEvent)) { + middleButtonData.setState(pickedNode, comp, offset.x, offset.y); + } + else if (SwingUtilities.isRightMouseButton(mEvent)) { + rightButtonData.setState(pickedNode, comp, offset.x, offset.y); } } } @@ -223,121 +238,143 @@ // 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 (pSwingMouseEvent.getID() == MouseEvent.MOUSE_DRAGGED - || pSwingMouseEvent.getID() == MouseEvent.MOUSE_RELEASED) { - - // LEFT MOUSE BUTTON - if (SwingUtilities.isLeftMouseButton(pSwingMouseEvent) && leftButtonData.getFocusedComponent() != null) { + if (isDragOrRelease(pSwingMouseEvent)) { + if (isLeftMouseButtonOnComponent(mEvent)) { handleButton(pSwingMouseEvent, aEvent, leftButtonData); } - // MIDDLE MOUSE BUTTON - if (SwingUtilities.isMiddleMouseButton(pSwingMouseEvent) && middleButtonData.getFocusedComponent() != null) { + if (isMiddleMouseButtonOnComponent(mEvent)) { handleButton(pSwingMouseEvent, aEvent, middleButtonData); } - // RIGHT MOUSE BUTTON - if (SwingUtilities.isRightMouseButton(pSwingMouseEvent) && rightButtonData.getFocusedComponent() != null) { + if (isRightMouseButtonOnComponent(mEvent)) { handleButton(pSwingMouseEvent, aEvent, rightButtonData); } } - // This case covers the cases mousePressed, mouseClicked, - // and mouseMoved events - else if ((pSwingMouseEvent.getID() == MouseEvent.MOUSE_PRESSED - || pSwingMouseEvent.getID() == MouseEvent.MOUSE_CLICKED || pSwingMouseEvent.getID() == MouseEvent.MOUSE_MOVED) - && comp != null) { + else if (isPressOrClickOrMove(pSwingMouseEvent) && comp != null) { + final MouseEvent e_temp = new MouseEvent(comp, pSwingMouseEvent.getID(), mEvent.getWhen(), mEvent + .getModifiers(), point.x - offset.x, point.y - offset.y, mEvent.getClickCount(), mEvent + .isPopupTrigger()); - final MouseEvent e_temp = new MouseEvent(comp, pSwingMouseEvent.getID(), pSwingMouseEvent.getWhen(), - pSwingMouseEvent.getModifiers(), (int) pt.getX() - offX, (int) pt.getY() - offY, pSwingMouseEvent - .getClickCount(), pSwingMouseEvent.isPopupTrigger()); + final PSwingEvent e2 = PSwingMouseEvent.createMouseEvent(e_temp.getID(), e_temp, aEvent); + dispatchEvent(comp, e2); + } + else if (isWheelEvent(pSwingMouseEvent) && comp != null) { + final MouseWheelEvent mWEvent = (MouseWheelEvent) mEvent; + final MouseWheelEvent e_temp = new MouseWheelEvent(comp, pSwingMouseEvent.getID(), mEvent.getWhen(), mEvent + .getModifiers(), point.x - offset.x, point.y - offset.y, mEvent.getClickCount(), mEvent + .isPopupTrigger(), mWEvent.getScrollType(), mWEvent.getScrollAmount(), mWEvent.getWheelRotation()); - final PSwingMouseEvent e2 = PSwingMouseEvent.createMouseEvent(e_temp.getID(), e_temp, aEvent); + final PSwingMouseWheelEvent e2 = new PSwingMouseWheelEvent(e_temp.getID(), e_temp, aEvent); dispatchEvent(comp, e2); } // 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) { + if (previousComponent != null) { // This means mouseExited // This shouldn't happen - since we're only getting node events if (comp == null || pSwingMouseEvent.getID() == MouseEvent.MOUSE_EXITED) { - final MouseEvent e_temp = createExitEvent(pSwingMouseEvent); + final MouseEvent e_temp = createExitEvent(mEvent); - final PSwingMouseEvent e2 = PSwingMouseEvent.createMouseEvent(e_temp.getID(), e_temp, aEvent); + final PSwingEvent e2 = PSwingMouseEvent.createMouseEvent(e_temp.getID(), e_temp, aEvent); - dispatchEvent(prevComponent, e2); - prevComponent = null; + dispatchEvent(previousComponent, e2); + previousComponent = null; } // This means mouseExited prevComponent and mouseEntered comp - else if (prevComponent != comp) { - MouseEvent e_temp = createExitEvent(pSwingMouseEvent); - PSwingMouseEvent e2 = PSwingMouseEvent.createMouseEvent(e_temp.getID(), e_temp, aEvent); - dispatchEvent(prevComponent, e2); + else if (previousComponent != comp) { + MouseEvent e_temp = createExitEvent(mEvent); + PSwingEvent e2 = PSwingMouseEvent.createMouseEvent(e_temp.getID(), e_temp, aEvent); + dispatchEvent(previousComponent, e2); - e_temp = createEnterEvent(comp, pSwingMouseEvent, offX, offY); + e_temp = createEnterEvent(comp, mEvent, offset.x, offset.y); e2 = PSwingMouseEvent.createMouseEvent(e_temp.getID(), e_temp, aEvent); - comp.dispatchEvent(e2); + comp.dispatchEvent(e2.asMouseEvent()); } } - else { - // This means mouseEntered - if (comp != null) { - final MouseEvent e_temp = createEnterEvent(comp, pSwingMouseEvent, offX, offY); - final PSwingMouseEvent e2 = PSwingMouseEvent.createMouseEvent(e_temp.getID(), e_temp, aEvent); - dispatchEvent(comp, e2); - } + else if (comp != null) { // This means mouseEntered + final MouseEvent e_temp = createEnterEvent(comp, mEvent, offset.x, offset.y); + final PSwingEvent e2 = PSwingMouseEvent.createMouseEvent(e_temp.getID(), e_temp, aEvent); + dispatchEvent(comp, e2); } - // todo add cursors - // // We have to manager our own Cursors since this is normally - // // done on the native side - // if( comp != cursorComponent && - // focusNodeLeft == null && - // focusNodeMiddle == null && - // focusNodeRight == null ) { - // if( comp != null ) { - // cursorComponent = comp; - // canvas.setCursor( comp.getCursor(), false ); - // } - // else { - // cursorComponent = null; - // canvas.resetCursor(); - // } - // } - - // Set the previous variables for next time - prevComponent = comp; + previousComponent = comp; if (comp != null) { - prevOff = new Point2D.Double(offX, offY); + previousOffset = offset; } } - private MouseEvent createEnterEvent(final Component comp, final PSwingMouseEvent e1, final int offX, final int offY) { + private Point extractSwingOffset(final Component comp, final PSwing swing) { + int offsetX = 0; + int offsetY = 0; + + for (Component c = comp; c != swing.getComponent(); c = c.getParent()) { + offsetX += c.getLocation().x; + offsetY += c.getLocation().y; + } + + return new Point(offsetX, offsetY); + } + + private boolean isRightMouseButtonOnComponent(final MouseEvent mEvent) { + return SwingUtilities.isRightMouseButton(mEvent) && rightButtonData.getFocusedComponent() != null; + } + + private boolean isMiddleMouseButtonOnComponent(final MouseEvent mEvent) { + return SwingUtilities.isMiddleMouseButton(mEvent) && middleButtonData.getFocusedComponent() != null; + } + + private boolean isLeftMouseButtonOnComponent(final MouseEvent mEvent) { + return SwingUtilities.isLeftMouseButton(mEvent) && leftButtonData.getFocusedComponent() != null; + } + + private boolean isMousePress(final PSwingEvent pSwingMouseEvent) { + return pSwingMouseEvent.getID() == MouseEvent.MOUSE_PRESSED; + } + + private boolean isWheelEvent(final PSwingEvent pSwingMouseEvent) { + return pSwingMouseEvent.getID() == MouseEvent.MOUSE_WHEEL; + } + + private boolean isPressOrClickOrMove(final PSwingEvent pSwingMouseEvent) { + return isMousePress(pSwingMouseEvent) || pSwingMouseEvent.getID() == MouseEvent.MOUSE_CLICKED + || pSwingMouseEvent.getID() == MouseEvent.MOUSE_MOVED; + } + + private boolean isDragOrRelease(final PSwingEvent pSwingMouseEvent) { + return pSwingMouseEvent.getID() == MouseEvent.MOUSE_DRAGGED + || pSwingMouseEvent.getID() == MouseEvent.MOUSE_RELEASED; + } + + private MouseEvent createEnterEvent(final Component comp, final MouseEvent e1, final int offX, final int offY) { return new MouseEvent(comp, MouseEvent.MOUSE_ENTERED, e1.getWhen(), 0, (int) prevPoint.getX() - offX, (int) prevPoint.getY() - offY, e1.getClickCount(), e1.isPopupTrigger()); } - private MouseEvent createExitEvent(final PSwingMouseEvent e1) { - return 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()); + private MouseEvent createExitEvent(final MouseEvent e1) { + return new MouseEvent(previousComponent, MouseEvent.MOUSE_EXITED, e1.getWhen(), 0, (int) prevPoint.getX() + - (int) previousOffset.getX(), (int) prevPoint.getY() - (int) previousOffset.getY(), + e1.getClickCount(), e1.isPopupTrigger()); } - private void handleButton(final PSwingMouseEvent e1, final PInputEvent aEvent, final ButtonData buttonData) { - Point2D pt; - if (buttonData.getPNode().isDescendentOf(canvas.getRoot())) { - pt = new Point2D.Double(e1.getX(), e1.getY()); - cameraToLocal(e1.getPath().getTopCamera(), pt, buttonData.getPNode()); - // todo this probably won't handle viewing through multiple cameras. - final MouseEvent e_temp = new MouseEvent(buttonData.getFocusedComponent(), e1.getID(), e1.getWhen(), e1 - .getModifiers(), (int) pt.getX() - buttonData.getOffsetX(), (int) pt.getY() - - buttonData.getOffsetY(), e1.getClickCount(), e1.isPopupTrigger()); + private void handleButton(final PSwingEvent e1, final PInputEvent aEvent, final ButtonData buttonData) { + final MouseEvent m1 = e1.asMouseEvent(); + if (involvesSceneNode(buttonData)) { + // TODO: this probably won't handle viewing through multiple + // cameras. - final PSwingMouseEvent e2 = PSwingMouseEvent.createMouseEvent(e_temp.getID(), e_temp, aEvent); + final Point2D pt = new Point2D.Double(m1.getX(), m1.getY()); + cameraToLocal(e1.getPath().getTopCamera(), pt, buttonData.getPNode()); + final MouseEvent tempEvent = new MouseEvent(buttonData.getFocusedComponent(), m1.getID(), m1.getWhen(), m1 + .getModifiers(), (int) pt.getX() - buttonData.getOffsetX(), (int) pt.getY() + - buttonData.getOffsetY(), m1.getClickCount(), m1.isPopupTrigger()); + + final PSwingEvent e2 = PSwingMouseEvent.createMouseEvent(tempEvent.getID(), tempEvent, aEvent); dispatchEvent(buttonData.getFocusedComponent(), e2); } else { @@ -346,46 +383,75 @@ // buttonData.getPSwing().repaint(); //Experiment with SliderExample // (from Martin) suggests this line is unnecessary, and a serious // problem in performance. - e1.consume(); + m1.consume(); if (e1.getID() == MouseEvent.MOUSE_RELEASED) { buttonData.mouseReleased(); } } - private void dispatchEvent(final Component target, final PSwingMouseEvent event) { + private boolean involvesSceneNode(final ButtonData buttonData) { + return buttonData.getPNode().isDescendentOf(canvas.getRoot()); + } + + private void dispatchEvent(final Component target, final PSwingEvent event) { SwingUtilities.invokeLater(new Runnable() { public void run() { - target.dispatchEvent(event); + target.dispatchEvent(event.asMouseEvent()); } }); } - private void cameraToLocal(final PCamera topCamera, final Point2D pt, final PNode node) { - AffineTransform inverse; - try { - inverse = topCamera.getViewTransform().createInverse(); - } - catch (final NoninvertibleTransformException e) { - throw new PAffineTransformException(e, topCamera.getViewTransform()); - } - - /* - * Only apply the camera's view transform when this node is a descendant - * of PLayer - */ - PNode searchNode = node; - do { - searchNode = searchNode.getParent(); - if (searchNode instanceof PLayer) { - inverse.transform(pt, pt); - break; - } - } while (searchNode != null); - + /** + * Transforms the given point from camera coordinates to the node's local + * system. + * + * @param camera camera from which coordinates are measured + * @param pt point to transform (will be modified) + * @param node node from which local coordinates are measured + */ + private void cameraToLocal(final PCamera camera, final Point2D pt, final PNode node) { if (node != null) { + if (descendsFromLayer(node)) { + final AffineTransform inverse = invertTransform(camera.getViewTransform()); + inverse.transform(pt, pt); + } + node.globalToLocal(pt); } - return; + } + + /** + * Returns true if the provided layer has a PLayer ancestor. + * + * @param node node being tested + * + * @return true if node is a descendant of a PLayer + */ + private boolean descendsFromLayer(final PNode node) { + PNode searchNode = node; + while (searchNode != null) { + searchNode = searchNode.getParent(); + if (searchNode instanceof PLayer) { + return true; + } + } + return false; + } + + /** + * Returns the inverse transform for the provided transform. Throws + * exception if transform is non invertible. + * + * @param transform transform to invert + * @return inverted transform + */ + private AffineTransform invertTransform(final PAffineTransform transform) { + try { + return transform.createInverse(); + } + catch (final NoninvertibleTransformException e) { + throw new PAffineTransformException(e, transform); + } } /** @@ -400,12 +466,12 @@ final InputEvent sourceSwingEvent = aEvent.getSourceSwingEvent(); if (sourceSwingEvent instanceof MouseEvent) { final MouseEvent swingMouseEvent = (MouseEvent) sourceSwingEvent; - final PSwingMouseEvent pSwingMouseEvent = PSwingMouseEvent.createMouseEvent(swingMouseEvent.getID(), + final PSwingEvent pSwingMouseEvent = PSwingMouseEvent.createMouseEvent(swingMouseEvent.getID(), swingMouseEvent, aEvent); if (!recursing) { recursing = true; dispatchEvent(pSwingMouseEvent, aEvent); - if (pSwingMouseEvent.isConsumed()) { + if (pSwingMouseEvent.asMouseEvent().isConsumed()) { aEvent.setHandled(true); } recursing = false; @@ -422,15 +488,12 @@ * Internal Utility class for handling button interactivity. */ private static class ButtonData { - private PSwing focusPSwing = null; private PNode focusNode = null; private Component focusComponent = null; private int focusOffX = 0; private int focusOffY = 0; - public void setState(final PSwing swing, final PNode visualNode, final Component comp, final int offX, - final int offY) { - focusPSwing = swing; + public void setState(final PNode visualNode, final Component comp, final int offX, final int offY) { focusComponent = comp; focusNode = visualNode; focusOffX = offX; @@ -453,10 +516,6 @@ return focusOffY; } - public PSwing getPSwing() { - return focusPSwing; - } - public void mouseReleased() { focusComponent = null; focusNode = null; diff --git a/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingMouseEvent.java b/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingMouseEvent.java index b3ab125..0144fbe 100644 --- a/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingMouseEvent.java +++ b/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingMouseEvent.java @@ -31,7 +31,7 @@ import java.awt.Component; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; -import java.awt.event.MouseMotionListener; +import java.awt.event.MouseWheelEvent; import java.awt.geom.Point2D; import java.io.Serializable; @@ -74,7 +74,7 @@ * @author Sam R. Reid * @author Lance E. Good */ -public class PSwingMouseEvent extends MouseEvent implements Serializable { +public class PSwingMouseEvent extends MouseEvent implements Serializable, PSwingEvent { /** * */ @@ -104,10 +104,13 @@ * @param e The original Java mouse event when in MOUSE_DRAGGED and * MOUSE_RELEASED events. */ - public static PSwingMouseEvent createMouseEvent(final int id, final MouseEvent e, final PInputEvent pEvent) { + public static PSwingEvent createMouseEvent(final int id, final MouseEvent e, final PInputEvent pEvent) { if (id == MouseEvent.MOUSE_MOVED || id == MouseEvent.MOUSE_DRAGGED) { return new PSwingMouseMotionEvent(id, e, pEvent); } + else if (id == MouseEvent.MOUSE_WHEEL) { + return new PSwingMouseWheelEvent(id, (MouseWheelEvent) e, pEvent); + } else { return new PSwingMouseEvent(id, e, pEvent); } @@ -228,40 +231,25 @@ * @param listener the MouseListener or MouseMotionListener to dispatch to. */ public void dispatchTo(final Object listener) { - if (listener instanceof MouseListener) { - final MouseListener mouseListener = (MouseListener) listener; - switch (getID()) { - case MouseEvent.MOUSE_CLICKED: - mouseListener.mouseClicked(this); - break; - case MouseEvent.MOUSE_ENTERED: - mouseListener.mouseEntered(this); - break; - case MouseEvent.MOUSE_EXITED: - mouseListener.mouseExited(this); - break; - case MouseEvent.MOUSE_PRESSED: - mouseListener.mousePressed(this); - break; - case MouseEvent.MOUSE_RELEASED: - mouseListener.mouseReleased(this); - break; - default: - throw new RuntimeException("PMouseEvent with bad ID"); - } - } - else { - final MouseMotionListener mouseMotionListener = (MouseMotionListener) listener; - switch (getID()) { - case MouseEvent.MOUSE_DRAGGED: - mouseMotionListener.mouseDragged(this); - break; - case MouseEvent.MOUSE_MOVED: - mouseMotionListener.mouseMoved(this); - break; - default: - throw new RuntimeException("PMouseMotionEvent with bad ID"); - } + final MouseListener mouseListener = (MouseListener) listener; + switch (getID()) { + case MouseEvent.MOUSE_CLICKED: + mouseListener.mouseClicked(this); + break; + case MouseEvent.MOUSE_ENTERED: + mouseListener.mouseEntered(this); + break; + case MouseEvent.MOUSE_EXITED: + mouseListener.mouseExited(this); + break; + case MouseEvent.MOUSE_PRESSED: + mouseListener.mousePressed(this); + break; + case MouseEvent.MOUSE_RELEASED: + mouseListener.mouseReleased(this); + break; + default: + throw new RuntimeException("PMouseEvent with bad ID"); } } @@ -275,4 +263,8 @@ public void setSource(final Object aSource) { source = aSource; } + + public MouseEvent asMouseEvent() { + return this; + } } \ No newline at end of file diff --git a/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingMouseWheelEvent.java b/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingMouseWheelEvent.java new file mode 100644 index 0000000..d5e4737 --- /dev/null +++ b/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingMouseWheelEvent.java @@ -0,0 +1,215 @@ +/** + * Copyright (C) 1998-2000 by University of Maryland, College Park, MD +20742, USA + * All rights reserved. + */ +package edu.umd.cs.piccolox.pswing; + +import java.awt.Component; +import java.awt.event.MouseEvent; +import java.awt.event.MouseWheelEvent; +import java.awt.event.MouseWheelListener; +import java.awt.geom.Point2D; + +import edu.umd.cs.piccolo.PNode; +import edu.umd.cs.piccolo.event.PInputEvent; +import edu.umd.cs.piccolo.util.PPickPath; + +/** + * PMouseMotionEvent is an event which indicates that a mouse motion + * action occurred in a node. + *

+ * This low-level event is generated by a node object for: + *

+ *

+ * A PMouseEvent object is passed to every PMouseMotionListener or + * PMouseMotionAdapter object which registered to receive mouse + * motion events using the component's addMouseMotionListener + * method. (PMouseMotionAdapter objects implement the + * PMouseMotionListener interface.) Each such listener object gets + * a PMouseEvent containing the mouse motion event. + *

+ *

+ * Warning: Serialized objects of this class will not be compatible with + * future Piccolo releases. The current serialization support is appropriate for + * short term storage or RMI between applications running the same version of + * Piccolo. A future release of Piccolo will provide support for long term + * persistence. + * + * @author Benjamin B. Bederson + * @author Sam R. Reid + * @author Lance E. Good + */ +public class PSwingMouseWheelEvent extends MouseWheelEvent implements PSwingEvent { + /** + * + */ + private static final long serialVersionUID = 1L; + private final int id; + private final PInputEvent event; + + /** + * Constructs a new PMouseWheel event from a Java MouseWheelEvent. + * + * @param id The event type (MOUSE_WHEEL) + * @param e The original Java mouse wheel event. + */ + protected PSwingMouseWheelEvent(final int id, final MouseWheelEvent e, final PInputEvent event) { + super((Component) e.getSource(), e.getID(), e.getWhen(), e.getModifiers(), e.getX(), e.getY(), e + .getClickCount(), e.isPopupTrigger(), e.getScrollType(), e.getScrollAmount(), e.getWheelRotation()); + this.id = id; + this.event = event; + } + + /** + * Returns the x,y position of the event in the local coordinate system of + * the node the event occurred on. + * + * @return a Point2D object containing the x and y coordinates local to the + * node. + */ + public Point2D getLocalPoint() { + return new Point2D.Double(getX(), getY()); + } + + /** + * Returns the horizontal x position of the event in the local coordinate + * system of the node the event occurred on. + * + * @return x a double indicating horizontal position local to the node. + */ + public double getLocalX() { + return getLocalPoint().getX(); + } + + /** + * Returns the vertical y position of the event in the local coordinate + * system of the node the event occurred on. + * + * @return y a double indicating vertical position local to the node. + */ + public double getLocalY() { + return getLocalPoint().getY(); + } + + /** + * Determine the event type. + * + * @return the id + */ + public int getID() { + return id; + } + + /** + * Determine the node the event originated at. If an event percolates up the + * tree and is handled by an event listener higher up in the tree than the + * original node that generated the event, this returns the original node. + * For mouse drag and release events, this is the node that the original + * matching press event went to - in other words, the event is 'grabbed' by + * the originating node. + * + * @return the node + */ + public PNode getNode() { + return event.getPickedNode(); + } + + /** + * Determine the path the event took from the PCanvas down to the visual + * component. + * + * @return the path + */ + public PPickPath getPath() { + return event.getPath(); + } + + /** + * Determine the node the event originated at. If an event percolates up the + * tree and is handled by an event listener higher up in the tree than the + * original node that generated the event, this returns the original node. + * For mouse drag and release events, this is the node that the original + * matching press event went to - in other words, the event is 'grabbed' by + * the originating node. + * + * @return the node + */ + public PNode getGrabNode() { + return event.getPickedNode(); + } + + /** + * Return the path from the PCanvas down to the currently grabbed object. + * + * @return the path + */ + public PPickPath getGrabPath() { + return getPath(); + } + + /** + * Get the current node that is under the cursor. This may return a + * different result then getGrabNode() when in a MOUSE_RELEASED or + * MOUSE_DRAGGED event. + * + * @return the current node. + */ + public PNode getCurrentNode() { + return event.getPickedNode(); + } + + /** + * Get the path from the PCanvas down to the visual component currently + * under the mouse.This may give a different result then getGrabPath() + * durring a MOUSE_DRAGGED or MOUSE_RELEASED operation. + * + * @return the current path. + */ + public PPickPath getCurrentPath() { + return getPath(); + } + + /** + * Calls appropriate method on the listener based on this events ID. + * + * @param listener the target for dispatch. + */ + public void dispatchTo(final Object listener) { + final MouseWheelListener mouseWheelListener = (MouseWheelListener) listener; + switch (getID()) { + case MouseEvent.MOUSE_WHEEL: + mouseWheelListener.mouseWheelMoved(this); + break; + default: + throw new RuntimeException("PMouseWheelEvent with bad ID"); + } + } + + /** + * Set the souce of this event. As the event is fired up the tree the source + * of the event will keep changing to reflect the scenegraph object that is + * firing the event. + * + * @param aSource + */ + public void setSource(final Object aSource) { + source = aSource; + } + + /** + * Returns this event as a mouse event. This reduces the need to cast + * instances of this interface when they are known to all extend MouseEvent. + * + * @return this object casted to a MouseEvent + */ + public MouseEvent asMouseEvent() { + return this; + } +} \ No newline at end of file diff --git a/extras/src/test/java/edu/umd/cs/piccolox/pswing/PSwingCanvasTest.java b/extras/src/test/java/edu/umd/cs/piccolox/pswing/PSwingCanvasTest.java index 4c6bb01..63b3a6a 100644 --- a/extras/src/test/java/edu/umd/cs/piccolox/pswing/PSwingCanvasTest.java +++ b/extras/src/test/java/edu/umd/cs/piccolox/pswing/PSwingCanvasTest.java @@ -40,11 +40,11 @@ public void setUp() { finalizerCallCount = 0; - } - + } + public void testRemovePSwingDoesNothingWithForeignPSwing() { - PSwingCanvas canvas = new PSwingCanvas(); - PSwing orphanPSwing = new PSwing(new JLabel()); + final PSwingCanvas canvas = new PSwingCanvas(); + final PSwing orphanPSwing = new PSwing(new JLabel()); canvas.removePSwing(orphanPSwing); } } diff --git a/extras/src/test/java/edu/umd/cs/piccolox/pswing/PSwingTest.java b/extras/src/test/java/edu/umd/cs/piccolox/pswing/PSwingTest.java index 3ff96d0..7ec0128 100644 --- a/extras/src/test/java/edu/umd/cs/piccolox/pswing/PSwingTest.java +++ b/extras/src/test/java/edu/umd/cs/piccolox/pswing/PSwingTest.java @@ -38,9 +38,8 @@ import javax.swing.JPanel; import javax.swing.RepaintManager; -import edu.umd.cs.piccolo.util.PPaintContext; - import junit.framework.TestCase; +import edu.umd.cs.piccolo.util.PPaintContext; /** * JUnit test class to exercise PSwing bugfixes. @@ -102,7 +101,8 @@ final BufferedImage img = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB); final Graphics2D graphics = GraphicsEnvironment.getLocalGraphicsEnvironment().createGraphics(img); - final PPaintContext paintContext = new PPaintContext(graphics);; + final PPaintContext paintContext = new PPaintContext(graphics); + ; pSwing.paintComponent(paintContext); assertEquals(Color.RED.getRGB(), img.getRGB(50, 50)); }