diff --git a/core/src/main/java/org/piccolo2d/event/PMouseWheelZoomEventHandler.java b/core/src/main/java/org/piccolo2d/event/PMouseWheelZoomEventHandler.java
new file mode 100644
index 0000000..16b0ea9
--- /dev/null
+++ b/core/src/main/java/org/piccolo2d/event/PMouseWheelZoomEventHandler.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2008-2012, Piccolo2D project, http://piccolo2d.org
+ * Copyright (c) 1998-2008, University of Maryland
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this list of conditions
+ * and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
+ * and the following disclaimer in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its
+ * contributors may be used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.piccolo2d.event;
+
+import java.awt.Rectangle;
+import java.awt.geom.Point2D;
+
+import org.piccolo2d.PCamera;
+import org.piccolo2d.PCanvas;
+
+/**
+ * Zoom event handler that scales the camera view transform in response to mouse wheel events.
+ *
+ * @since 2.0
+ */
+public final class PMouseWheelZoomEventHandler extends PBasicInputEventHandler {
+ /** Default scale factor, 0.1d
. */
+ static final double DEFAULT_SCALE_FACTOR = 0.1d;
+
+ /** Scale factor. */
+ private double scaleFactor = DEFAULT_SCALE_FACTOR;
+
+ /** Zoom mode. */
+ private ZoomMode zoomMode = ZoomMode.ZOOM_ABOUT_CANVAS_CENTER;
+
+
+ /**
+ * Create a new mouse wheel zoom event handler.
+ */
+ public PMouseWheelZoomEventHandler() {
+ super();
+ PInputEventFilter eventFilter = new PInputEventFilter();
+ eventFilter.rejectAllEventTypes();
+ eventFilter.setAcceptsMouseWheelRotated(true);
+ setEventFilter(eventFilter);
+ }
+
+
+ /**
+ * Return the scale factor for this mouse wheel zoom event handler. Defaults to DEFAULT_SCALE_FACTOR
.
+ *
+ * @see #DEFAULT_SCALE_FACTOR
+ * @return the scale factor for this mouse wheel zoom event handler
+ */
+ public double getScaleFactor() {
+ return scaleFactor;
+ }
+
+ /**
+ * Set the scale factor for this mouse wheel zoom event handler to scaleFactor
.
+ *
+ * @param scaleFactor scale factor for this mouse wheel zoom event handler
+ */
+ public void setScaleFactor(final double scaleFactor) {
+ this.scaleFactor = scaleFactor;
+ }
+
+ /**
+ * Switch to zoom about mouse mode.
+ *
+ * @see ZoomMode#ZOOM_ABOUT_MOUSE
+ */
+ public void zoomAboutMouse() {
+ zoomMode = ZoomMode.ZOOM_ABOUT_MOUSE;
+ }
+
+ /**
+ * Switch to zoom about canvas center mode.
+ *
+ * @see ZoomMode#ZOOM_ABOUT_CANVAS_CENTER
+ */
+ public void zoomAboutCanvasCenter() {
+ zoomMode = ZoomMode.ZOOM_ABOUT_CANVAS_CENTER;
+ }
+
+ /**
+ * Switch to zoom about view center mode.
+ *
+ * @see ZoomMode#ZOOM_ABOUT_VIEW_CENTER
+ */
+ public void zoomAboutViewCenter() {
+ zoomMode = ZoomMode.ZOOM_ABOUT_VIEW_CENTER;
+ }
+
+ /**
+ * Return the zoom mode for this mouse wheel zoom event handler. Defaults to
+ * ZoomMode.ZOOM_ABOUT_CANVAS_CENTER
.
+ *
+ * @return the zoom mode for this mouse wheel zoom event handler
+ */
+ ZoomMode getZoomMode() {
+ return zoomMode;
+ }
+
+ /** {@inheritDoc} */
+ public void mouseWheelRotated(final PInputEvent event) {
+ PCamera camera = event.getCamera();
+ double scale = 1.0d + event.getWheelRotation() * scaleFactor;
+ Point2D viewAboutPoint = getViewAboutPoint(event);
+ camera.scaleViewAboutPoint(scale, viewAboutPoint.getX(), viewAboutPoint.getY());
+ }
+
+ /**
+ * Return the view about point for the specified event according to the current zoom mode.
+ *
+ * @param event input event
+ * @return the view about point for the specified event according to the current zoom mode
+ */
+ private Point2D getViewAboutPoint(final PInputEvent event) {
+ switch (zoomMode) {
+ case ZOOM_ABOUT_MOUSE:
+ return event.getPosition();
+ case ZOOM_ABOUT_CANVAS_CENTER:
+ Rectangle canvasBounds = ((PCanvas) event.getComponent()).getBounds();
+ Point2D canvasCenter = new Point2D.Double(canvasBounds.getCenterX(), canvasBounds.getCenterY());
+ event.getPath().canvasToLocal(canvasCenter, event.getCamera());
+ return event.getCamera().localToView(canvasCenter);
+ case ZOOM_ABOUT_VIEW_CENTER:
+ return event.getCamera().getBoundsReference().getCenter2D();
+ }
+ throw new IllegalArgumentException("illegal zoom mode " + zoomMode);
+ }
+
+ /**
+ * Zoom mode.
+ */
+ enum ZoomMode {
+ /**
+ * Zoom about mouse mode.
+ */
+ ZOOM_ABOUT_MOUSE,
+
+ /**
+ * Zoom about canvas center mode.
+ */
+ ZOOM_ABOUT_CANVAS_CENTER,
+
+ /**
+ * Zoom about view center mode.
+ */
+ ZOOM_ABOUT_VIEW_CENTER;
+ }
+}
diff --git a/core/src/test/java/org/piccolo2d/event/PMouseWheelZoomEventHandlerTest.java b/core/src/test/java/org/piccolo2d/event/PMouseWheelZoomEventHandlerTest.java
new file mode 100644
index 0000000..eab0eb0
--- /dev/null
+++ b/core/src/test/java/org/piccolo2d/event/PMouseWheelZoomEventHandlerTest.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2008-2012, Piccolo2D project, http://piccolo2d.org
+ * Copyright (c) 1998-2008, University of Maryland
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this list of conditions
+ * and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
+ * and the following disclaimer in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its
+ * contributors may be used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.piccolo2d.event;
+
+import static org.piccolo2d.event.PMouseWheelZoomEventHandler.DEFAULT_SCALE_FACTOR;
+import static org.piccolo2d.event.PMouseWheelZoomEventHandler.ZoomMode.ZOOM_ABOUT_MOUSE;
+import static org.piccolo2d.event.PMouseWheelZoomEventHandler.ZoomMode.ZOOM_ABOUT_CANVAS_CENTER;
+import static org.piccolo2d.event.PMouseWheelZoomEventHandler.ZoomMode.ZOOM_ABOUT_VIEW_CENTER;
+
+import junit.framework.TestCase;
+
+/**
+ * Unit test for PMouseWheelZoomEventHandler.
+ */
+public final class PMouseWheelZoomEventHandlerTest extends TestCase {
+ private static final double TOLERANCE = 0.1d;
+
+ public void testConstructor() {
+ assertNotNull(new PMouseWheelZoomEventHandler());
+ }
+
+ public void testScaleFactor() {
+ PMouseWheelZoomEventHandler zoomHandler = new PMouseWheelZoomEventHandler();
+ assertEquals(DEFAULT_SCALE_FACTOR, zoomHandler.getScaleFactor(), TOLERANCE);
+
+ zoomHandler.setScaleFactor(42.0d);
+ assertEquals(42.0d, zoomHandler.getScaleFactor(), TOLERANCE);
+ }
+
+ public void testDefaultZoomMode() {
+ PMouseWheelZoomEventHandler zoomHandler = new PMouseWheelZoomEventHandler();
+ assertSame(ZOOM_ABOUT_CANVAS_CENTER, zoomHandler.getZoomMode());
+ }
+
+ public void testZoomAboutMouse() {
+ PMouseWheelZoomEventHandler zoomHandler = new PMouseWheelZoomEventHandler();
+ zoomHandler.zoomAboutMouse();
+ assertSame(ZOOM_ABOUT_MOUSE, zoomHandler.getZoomMode());
+ }
+
+ public void testZoomAboutCanvasCenter() {
+ PMouseWheelZoomEventHandler zoomHandler = new PMouseWheelZoomEventHandler();
+ zoomHandler.zoomAboutCanvasCenter();
+ assertSame(ZOOM_ABOUT_CANVAS_CENTER, zoomHandler.getZoomMode());
+ }
+
+ public void testZoomAboutViewCenter() {
+ PMouseWheelZoomEventHandler zoomHandler = new PMouseWheelZoomEventHandler();
+ zoomHandler.zoomAboutViewCenter();
+ assertSame(ZOOM_ABOUT_VIEW_CENTER, zoomHandler.getZoomMode());
+ }
+}
diff --git a/examples/src/main/java/org/piccolo2d/examples/ExampleRunner.java b/examples/src/main/java/org/piccolo2d/examples/ExampleRunner.java
index 8bc46b0..adaf147 100644
--- a/examples/src/main/java/org/piccolo2d/examples/ExampleRunner.java
+++ b/examples/src/main/java/org/piccolo2d/examples/ExampleRunner.java
@@ -76,13 +76,13 @@
ClipExample.class, CompositeExample.class, DynamicExample.class, EventHandlerExample.class,
FullScreenNodeExample.class, GraphEditorExample.class, GridExample.class, GroupExample.class,
HandleExample.class, HelloWorldExample.class, HierarchyZoomExample.class, HtmlViewExample.class,
- KeyEventFocusExample.class, LayoutExample.class, LensExample.class, NavigationExample.class,
- NodeCacheExample.class, NodeEventExample.class, NodeExample.class, NodeLinkExample.class,
- P3DRectExample.class, PanToExample.class, PathExample.class, PositionExample.class,
- PositionPathActivityExample.class, PulseExample.class, ScrollingExample.class, SelectionExample.class,
- ShadowExample.class, SquiggleExample.class, StickyExample.class, StickyHandleLayerExample.class,
- StrokeExample.class, TextExample.class, TooltipExample.class, TwoCanvasExample.class,
- WaitForActivitiesExample.class });
+ KeyEventFocusExample.class, LayoutExample.class, LensExample.class, MouseWheelZoomExample.class,
+ NavigationExample.class, NodeCacheExample.class, NodeEventExample.class, NodeExample.class,
+ NodeLinkExample.class, P3DRectExample.class, PanToExample.class, PathExample.class,
+ PositionExample.class, PositionPathActivityExample.class, PulseExample.class, ScrollingExample.class,
+ SelectionExample.class, ShadowExample.class, SquiggleExample.class, StickyExample.class,
+ StickyHandleLayerExample.class, StrokeExample.class, TextExample.class, TooltipExample.class,
+ TwoCanvasExample.class, WaitForActivitiesExample.class });
}
/**
diff --git a/examples/src/main/java/org/piccolo2d/examples/MouseWheelZoomExample.java b/examples/src/main/java/org/piccolo2d/examples/MouseWheelZoomExample.java
new file mode 100644
index 0000000..1cfb8e9
--- /dev/null
+++ b/examples/src/main/java/org/piccolo2d/examples/MouseWheelZoomExample.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (c) 2008-2012, Piccolo2D project, http://piccolo2d.org
+ * Copyright (c) 1998-2008, University of Maryland
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this list of conditions
+ * and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
+ * and the following disclaimer in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its
+ * contributors may be used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.piccolo2d.examples;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Paint;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.Random;
+
+import javax.swing.ButtonGroup;
+import javax.swing.JPanel;
+import javax.swing.JToggleButton;
+import javax.swing.JToolBar;
+
+import org.piccolo2d.PCanvas;
+import org.piccolo2d.event.PMouseWheelZoomEventHandler;
+import org.piccolo2d.extras.PFrame;
+import org.piccolo2d.nodes.PPath;
+import org.piccolo2d.nodes.PText;
+
+/**
+ * Mouse wheel zoom example.
+ *
+ * @since 2.0
+ */
+public final class MouseWheelZoomExample extends PFrame {
+ /** Default serial version UID. */
+ private static final long serialVersionUID = 1L;
+
+ /** Number of text and rect nodes. */
+ private static final int N = 100;
+
+ /** Text paint. */
+ private static final Paint TEXT_PAINT = new Color(20, 20, 20, 200);
+
+ /** Source of randomness. */
+ private final Random random;
+
+
+ /**
+ * Create a new mouse wheel zoom example.
+ */
+ public MouseWheelZoomExample() {
+ this(null);
+ }
+
+ /**
+ * Create a new mouse wheel zoom example with the specified canvas.
+ *
+ * @param canvas canvas for this mouse wheel zoom example
+ */
+ public MouseWheelZoomExample(final PCanvas canvas) {
+ super("MouseWheelZoomExample", false, canvas);
+ random = new Random();
+ }
+
+
+ /** {@inheritDoc} */
+ public void initialize() {
+ for (int i = 0; i < N; i++) {
+ createTextNode(i);
+ }
+ for (int i = 0; i < N; i++) {
+ createRectNode(i);
+ }
+
+ // uninstall default zoom event handler
+ getCanvas().removeInputEventListener(getCanvas().getZoomEventHandler());
+
+ // install mouse wheel zoom event handler
+ final PMouseWheelZoomEventHandler mouseWheelZoomEventHandler = new PMouseWheelZoomEventHandler();
+ getCanvas().addInputEventListener(mouseWheelZoomEventHandler);
+
+ // create a toolbar
+ final JToolBar toolBar = new JToolBar();
+ final JToggleButton zoomAboutMouse = new JToggleButton("Zoom about mouse");
+ final JToggleButton zoomAboutCanvasCenter = new JToggleButton("Zoom about canvas center");
+ final JToggleButton zoomAboutViewCenter = new JToggleButton("Zoom about view center");
+ final ButtonGroup buttonGroup = new ButtonGroup();
+ buttonGroup.add(zoomAboutMouse);
+ buttonGroup.add(zoomAboutCanvasCenter);
+ buttonGroup.add(zoomAboutViewCenter);
+ toolBar.add(zoomAboutMouse);
+ toolBar.add(zoomAboutCanvasCenter);
+ toolBar.add(zoomAboutViewCenter);
+ toolBar.setFloatable(false);
+
+ zoomAboutMouse.addActionListener(new ActionListener() {
+ /** {@inheritDoc} */
+ public void actionPerformed(final ActionEvent event) {
+ mouseWheelZoomEventHandler.zoomAboutMouse();
+ }
+ });
+ zoomAboutCanvasCenter.addActionListener(new ActionListener() {
+ /** {@inheritDoc} */
+ public void actionPerformed(final ActionEvent event) {
+ mouseWheelZoomEventHandler.zoomAboutCanvasCenter();
+ }
+ });
+ zoomAboutViewCenter.addActionListener(new ActionListener() {
+ /** {@inheritDoc} */
+ public void actionPerformed(final ActionEvent event) {
+ mouseWheelZoomEventHandler.zoomAboutViewCenter();
+ }
+ });
+
+ final JPanel contentPane = new JPanel();
+ contentPane.setLayout(new BorderLayout());
+ contentPane.add("North", toolBar);
+ contentPane.add("Center", getCanvas());
+ setContentPane(contentPane);
+ validate();
+ }
+
+ private void createTextNode(final int i) {
+ PText text = new PText("Label " + i);
+ text.setTextPaint(TEXT_PAINT);
+ text.setOffset(random.nextDouble() * 1000.0d - random.nextDouble() * 1000.0,
+ random.nextDouble() * 1000.0d - random.nextDouble() * 1000.0);
+ getCanvas().getLayer().addChild(text);
+ }
+
+ private void createRectNode(final int i) {
+ PPath path = PPath.createRectangle(0.0f, 0.0f, 50.0f * i/N + 10.0f, 50.0f * i/N + 10.0f);
+ int r = 200 * i/N + 45;
+ path.setPaint(new Color(r, 0, 0, 200));
+ path.setStrokePaint(new Color(r - 10, 0, 0, 200));
+ path.setOffset(random.nextDouble() * 1000.0d - random.nextDouble() * 1000.0,
+ random.nextDouble() * 1000.0d - random.nextDouble() * 1000.0);
+ getCanvas().getLayer().addChild(path);
+ }
+
+
+ /**
+ * Main.
+ *
+ * @param args command line arguments, ignored
+ */
+ public static void main(final String[] args) {
+ new MouseWheelZoomExample();
+ }
+}
\ No newline at end of file