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.
+ * 
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));
     }