diff --git a/swt-examples/src/main/java/edu/umd/cs/piccolox/swt/examples/SWTBasicExample.java b/swt-examples/src/main/java/edu/umd/cs/piccolox/swt/examples/SWTBasicExample.java
index 76584a8..edfe675 100644
--- a/swt-examples/src/main/java/edu/umd/cs/piccolox/swt/examples/SWTBasicExample.java
+++ b/swt-examples/src/main/java/edu/umd/cs/piccolox/swt/examples/SWTBasicExample.java
@@ -33,10 +33,10 @@
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
+import org.piccolo2d.extras.swt.PSWTCanvas;
+import org.piccolo2d.extras.swt.PSWTPath;
+import org.piccolo2d.extras.swt.PSWTText;
-import edu.umd.cs.piccolox.swt.PSWTCanvas;
-import edu.umd.cs.piccolox.swt.PSWTPath;
-import edu.umd.cs.piccolox.swt.PSWTText;
public final class SWTBasicExample {
diff --git a/swt-examples/src/main/java/edu/umd/cs/piccolox/swt/examples/SWTBenchTest.java b/swt-examples/src/main/java/edu/umd/cs/piccolox/swt/examples/SWTBenchTest.java
index 25d4847..5e407d2 100644
--- a/swt-examples/src/main/java/edu/umd/cs/piccolox/swt/examples/SWTBenchTest.java
+++ b/swt-examples/src/main/java/edu/umd/cs/piccolox/swt/examples/SWTBenchTest.java
@@ -53,8 +53,8 @@
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
+import org.piccolo2d.extras.swt.SWTGraphics2D;
-import edu.umd.cs.piccolox.swt.SWTGraphics2D;
/**
* Piccolo2D SWT benchmarking test suite.
diff --git a/swt-examples/src/main/java/edu/umd/cs/piccolox/swt/examples/SWTHelloWorld.java b/swt-examples/src/main/java/edu/umd/cs/piccolox/swt/examples/SWTHelloWorld.java
index 74ed0d9..cf737f6 100644
--- a/swt-examples/src/main/java/edu/umd/cs/piccolox/swt/examples/SWTHelloWorld.java
+++ b/swt-examples/src/main/java/edu/umd/cs/piccolox/swt/examples/SWTHelloWorld.java
@@ -31,9 +31,9 @@
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
+import org.piccolo2d.extras.swt.PSWTCanvas;
+import org.piccolo2d.extras.swt.PSWTText;
-import edu.umd.cs.piccolox.swt.PSWTCanvas;
-import edu.umd.cs.piccolox.swt.PSWTText;
/**
* Piccolo2D SWT Hello World example.
diff --git a/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTBoundsHandle.java b/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTBoundsHandle.java
deleted file mode 100644
index ce50e14..0000000
--- a/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTBoundsHandle.java
+++ /dev/null
@@ -1,430 +0,0 @@
-/*
- * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org
- * Copyright (c) 1998-2008, University of Maryland
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification, are permitted provided
- * that the following conditions are met:
- *
- * Redistributions of source code must retain the above copyright notice, this list of conditions
- * and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
- * and the following disclaimer in the documentation and/or other materials provided with the
- * distribution.
- *
- * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its
- * contributors may be used to endorse or promote products derived from this software without specific
- * prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
- * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
- * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package edu.umd.cs.piccolox.swt;
-
-import java.awt.Cursor;
-import java.awt.geom.Point2D;
-import java.util.ArrayList;
-import java.util.Iterator;
-
-import javax.swing.SwingConstants;
-
-import org.piccolo2d.PCamera;
-import org.piccolo2d.PNode;
-import org.piccolo2d.event.PBasicInputEventHandler;
-import org.piccolo2d.event.PInputEvent;
-import org.piccolo2d.extras.util.PBoundsLocator;
-import org.piccolo2d.util.PBounds;
-import org.piccolo2d.util.PDimension;
-import org.piccolo2d.util.PPickPath;
-
-
-/**
- * PSWTBoundsHandle a handle for resizing the bounds of another node. If a
- * bounds handle is dragged such that the other node's width or height becomes
- * negative then the each drag handle's locator assciated with that other node
- * is "flipped" so that they are attached to and dragging a different corner of
- * the nodes bounds.
- *
- * @version 1.0
- * @author Jesse Grosjean
- */
-public class PSWTBoundsHandle extends PSWTHandle {
- private final class HandleCursorEventHandler extends PBasicInputEventHandler {
- boolean cursorPushed = false;
-
- public void mouseEntered(final PInputEvent aEvent) {
- if (!cursorPushed) {
- aEvent.pushCursor(getCursorFor(((PBoundsLocator) getLocator()).getSide()));
- cursorPushed = true;
- }
- }
-
- public void mouseExited(final PInputEvent aEvent) {
- final PPickPath focus = aEvent.getInputManager().getMouseFocus();
-
- if (cursorPushed && isNewFocus(focus)) {
- aEvent.popCursor();
- cursorPushed = false;
- }
- }
-
- private boolean isNewFocus(final PPickPath focus) {
- return (focus == null || focus.getPickedNode() != PSWTBoundsHandle.this);
- }
-
- public void mouseReleased(final PInputEvent event) {
- if (cursorPushed) {
- event.popCursor();
- cursorPushed = false;
- }
- }
- }
-
- private static final long serialVersionUID = 1L;
- private PBasicInputEventHandler handleCursorHandler;
-
- /**
- * Adds bounds handles to all corners and edges of the provided node.
- *
- * @param node to decorate with bounds handles.
- */
- public static void addBoundsHandlesTo(final PNode node) {
- node.addChild(new PSWTBoundsHandle(PBoundsLocator.createEastLocator(node)));
- node.addChild(new PSWTBoundsHandle(PBoundsLocator.createWestLocator(node)));
- node.addChild(new PSWTBoundsHandle(PBoundsLocator.createNorthLocator(node)));
- node.addChild(new PSWTBoundsHandle(PBoundsLocator.createSouthLocator(node)));
- node.addChild(new PSWTBoundsHandle(PBoundsLocator.createNorthEastLocator(node)));
- node.addChild(new PSWTBoundsHandle(PBoundsLocator.createNorthWestLocator(node)));
- node.addChild(new PSWTBoundsHandle(PBoundsLocator.createSouthEastLocator(node)));
- node.addChild(new PSWTBoundsHandle(PBoundsLocator.createSouthWestLocator(node)));
- }
-
- /**
- * Adds sticky bounds handles to all corners and edges of the provided node
- * and for display on the provided camera.
- *
- * @param node to decorate with bounds handles.
- * @param camera camera onto which the handles should be stuck
- */
- public static void addStickyBoundsHandlesTo(final PNode node, final PCamera camera) {
- camera.addChild(new PSWTBoundsHandle(PBoundsLocator.createEastLocator(node)));
- camera.addChild(new PSWTBoundsHandle(PBoundsLocator.createWestLocator(node)));
- camera.addChild(new PSWTBoundsHandle(PBoundsLocator.createNorthLocator(node)));
- camera.addChild(new PSWTBoundsHandle(PBoundsLocator.createSouthLocator(node)));
- camera.addChild(new PSWTBoundsHandle(PBoundsLocator.createNorthEastLocator(node)));
- camera.addChild(new PSWTBoundsHandle(PBoundsLocator.createNorthWestLocator(node)));
- camera.addChild(new PSWTBoundsHandle(PBoundsLocator.createSouthEastLocator(node)));
- camera.addChild(new PSWTBoundsHandle(PBoundsLocator.createSouthWestLocator(node)));
- }
-
- /**
- * Removes all bounds handles from the specified node.
- *
- * @param node node from which to remove bounds handles
- */
- public static void removeBoundsHandlesFrom(final PNode node) {
- final ArrayList handles = new ArrayList();
-
- final Iterator i = node.getChildrenIterator();
- while (i.hasNext()) {
- final PNode each = (PNode) i.next();
- if (each instanceof PSWTBoundsHandle) {
- handles.add(each);
- }
- }
- node.removeChildren(handles);
- }
-
- /**
- * Creates a bounds handle that will use the provided bounds locator to
- * position itself.
- *
- * @param locator locator to use when positioning this handle
- */
- public PSWTBoundsHandle(final PBoundsLocator locator) {
- super(locator);
- }
-
- /**
- * Installs handlers responsible for updating the attached node's bounds and
- * for updating the cursor when the mous enters a handle.
- */
- protected void installHandleEventHandlers() {
- super.installHandleEventHandlers();
- handleCursorHandler = new HandleCursorEventHandler();
- addInputEventListener(handleCursorHandler);
- }
-
- /**
- * Return the event handler that is responsible for setting the mouse cursor
- * when it enters/exits this handle.
- *
- * @return handler responsible for keeping the mouse cursor up to date
- */
- public PBasicInputEventHandler getHandleCursorEventHandler() {
- return handleCursorHandler;
- }
-
- /**
- * Callback invoked when the user has started to drag a handle.
- *
- * @param aLocalPoint point in the handle's coordinate system at which the
- * drag was started
- * @param aEvent Piccolo2d Event representing the start of the drag
- */
- public void startHandleDrag(final Point2D aLocalPoint, final PInputEvent aEvent) {
- final PBoundsLocator l = (PBoundsLocator) getLocator();
- l.getNode().startResizeBounds();
- }
-
- /**
- * Callback invoked when the user is dragging the handle. Updates the
- * associated node appropriately.
- *
- * @param aLocalDimension magnitude of drag in the handle's coordinate
- * system
- * @param aEvent Piccolo2d Event representing the start of the drag
- */
- public void dragHandle(final PDimension aLocalDimension, final PInputEvent aEvent) {
- final PBoundsLocator l = (PBoundsLocator) getLocator();
-
- final PNode n = l.getNode();
- final PBounds b = n.getBounds();
-
- final PNode parent = getParent();
- if (parent != n && parent instanceof PCamera) {
- ((PCamera) parent).localToView(aLocalDimension);
- }
-
- localToGlobal(aLocalDimension);
- n.globalToLocal(aLocalDimension);
-
- final double dx = aLocalDimension.getWidth();
- final double dy = aLocalDimension.getHeight();
-
- switch (l.getSide()) {
- case SwingConstants.NORTH:
- b.setRect(b.x, b.y + dy, b.width, b.height - dy);
- break;
-
- case SwingConstants.SOUTH:
- b.setRect(b.x, b.y, b.width, b.height + dy);
- break;
-
- case SwingConstants.EAST:
- b.setRect(b.x, b.y, b.width + dx, b.height);
- break;
-
- case SwingConstants.WEST:
- b.setRect(b.x + dx, b.y, b.width - dx, b.height);
- break;
-
- case SwingConstants.NORTH_WEST:
- b.setRect(b.x + dx, b.y + dy, b.width - dx, b.height - dy);
- break;
-
- case SwingConstants.SOUTH_WEST:
- b.setRect(b.x + dx, b.y, b.width - dx, b.height + dy);
- break;
-
- case SwingConstants.NORTH_EAST:
- b.setRect(b.x, b.y + dy, b.width + dx, b.height - dy);
- break;
-
- case SwingConstants.SOUTH_EAST:
- b.setRect(b.x, b.y, b.width + dx, b.height + dy);
- break;
- default:
- // Leave bounds untouched
- }
-
- boolean flipX = false;
- boolean flipY = false;
-
- if (b.width < 0) {
- flipX = true;
- b.width = -b.width;
- b.x -= b.width;
- }
-
- if (b.height < 0) {
- flipY = true;
- b.height = -b.height;
- b.y -= b.height;
- }
-
- if (flipX || flipY) {
- flipSiblingBoundsHandles(flipX, flipY);
- }
-
- n.setBounds(b);
- }
-
- /**
- * Callback invoked when the handle stops being dragged.
- *
- * @param aLocalPoint point in the handle's coordinate system at which the
- * drag was stopped
- * @param aEvent Piccolo2d Event representing the stop of the drag
- */
- public void endHandleDrag(final Point2D aLocalPoint, final PInputEvent aEvent) {
- final PBoundsLocator l = (PBoundsLocator) getLocator();
- l.getNode().endResizeBounds();
- }
-
- /**
- * Iterates over all of this node's handles flipping them if necessary. This
- * is needed since a node can become inverted when it's width or height
- * becomes negative.
- *
- * @param flipX whether to allow flipping in the horizontal direction
- * @param flipY whether to allow flipping in the vertical direction
- */
- public void flipSiblingBoundsHandles(final boolean flipX, final boolean flipY) {
- final Iterator i = getParent().getChildrenIterator();
- while (i.hasNext()) {
- final Object each = i.next();
- if (each instanceof PSWTBoundsHandle) {
- ((PSWTBoundsHandle) each).flipHandleIfNeeded(flipX, flipY);
- }
- }
- }
-
- /**
- * Flips this particular handle around if needed. This is necessary since a
- * node can become inverted when it's width or height becomes negative.
- *
- * @param flipX whether to allow flipping in the horizontal direction
- * @param flipY whether to allow flipping in the vertical direction
- */
- public void flipHandleIfNeeded(final boolean flipX, final boolean flipY) {
- if (!flipX && !flipY) {
- return;
- }
-
- final PBoundsLocator l = (PBoundsLocator) getLocator();
- switch (l.getSide()) {
- case SwingConstants.NORTH:
- if (flipY) {
- l.setSide(SwingConstants.SOUTH);
- }
- break;
-
- case SwingConstants.SOUTH:
- if (flipY) {
- l.setSide(SwingConstants.NORTH);
- }
- break;
-
- case SwingConstants.EAST:
- if (flipX) {
- l.setSide(SwingConstants.WEST);
- }
- break;
-
- case SwingConstants.WEST:
- if (flipX) {
- l.setSide(SwingConstants.EAST);
- }
- break;
-
- case SwingConstants.NORTH_WEST:
- if (flipX && flipY) {
- l.setSide(SwingConstants.SOUTH_EAST);
- }
- else if (flipX) {
- l.setSide(SwingConstants.NORTH_EAST);
- }
- else if (flipY) {
- l.setSide(SwingConstants.SOUTH_WEST);
- }
- break;
-
- case SwingConstants.SOUTH_WEST:
- if (flipX && flipY) {
- l.setSide(SwingConstants.NORTH_EAST);
- }
- else if (flipX) {
- l.setSide(SwingConstants.SOUTH_EAST);
- }
- else if (flipY) {
- l.setSide(SwingConstants.NORTH_WEST);
- }
- break;
-
- case SwingConstants.NORTH_EAST:
- if (flipX && flipY) {
- l.setSide(SwingConstants.SOUTH_WEST);
- }
- else if (flipX) {
- l.setSide(SwingConstants.NORTH_WEST);
- }
- else if (flipY) {
- l.setSide(SwingConstants.SOUTH_EAST);
- }
- break;
-
- case SwingConstants.SOUTH_EAST:
- if (flipX && flipY) {
- l.setSide(SwingConstants.NORTH_WEST);
- }
- else if (flipX) {
- l.setSide(SwingConstants.SOUTH_WEST);
- }
- else if (flipY) {
- l.setSide(SwingConstants.NORTH_EAST);
- }
- break;
- default:
- // Do nothing
- }
-
- // reset locator to update layout
- setLocator(l);
- }
-
- /**
- * Returns an appropriate cursor to display when the mouse is over a handle
- * on the side provided.
- *
- * @param side value from SwingConstants
- *
- * @return Appropriate cursor, or null if no appropriate cursor can be found
- */
- public Cursor getCursorFor(final int side) {
- switch (side) {
- case SwingConstants.NORTH:
- return new Cursor(Cursor.N_RESIZE_CURSOR);
-
- case SwingConstants.SOUTH:
- return new Cursor(Cursor.S_RESIZE_CURSOR);
-
- case SwingConstants.EAST:
- return new Cursor(Cursor.E_RESIZE_CURSOR);
-
- case SwingConstants.WEST:
- return new Cursor(Cursor.W_RESIZE_CURSOR);
-
- case SwingConstants.NORTH_WEST:
- return new Cursor(Cursor.NW_RESIZE_CURSOR);
-
- case SwingConstants.SOUTH_WEST:
- return new Cursor(Cursor.SW_RESIZE_CURSOR);
-
- case SwingConstants.NORTH_EAST:
- return new Cursor(Cursor.NE_RESIZE_CURSOR);
-
- case SwingConstants.SOUTH_EAST:
- return new Cursor(Cursor.SE_RESIZE_CURSOR);
- default:
- return null;
- }
- }
-}
diff --git a/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTCanvas.java b/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTCanvas.java
deleted file mode 100644
index 47e9941..0000000
--- a/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTCanvas.java
+++ /dev/null
@@ -1,738 +0,0 @@
-/*
- * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org
- * Copyright (c) 1998-2008, University of Maryland
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification, are permitted provided
- * that the following conditions are met:
- *
- * Redistributions of source code must retain the above copyright notice, this list of conditions
- * and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
- * and the following disclaimer in the documentation and/or other materials provided with the
- * distribution.
- *
- * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its
- * contributors may be used to endorse or promote products derived from this software without specific
- * prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
- * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
- * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package edu.umd.cs.piccolox.swt;
-
-import java.awt.Color;
-import java.awt.Graphics2D;
-import java.awt.event.InputEvent;
-import java.awt.geom.Rectangle2D;
-
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.DisposeEvent;
-import org.eclipse.swt.events.DisposeListener;
-import org.eclipse.swt.events.KeyEvent;
-import org.eclipse.swt.events.KeyListener;
-import org.eclipse.swt.events.MouseEvent;
-import org.eclipse.swt.events.MouseListener;
-import org.eclipse.swt.events.MouseMoveListener;
-import org.eclipse.swt.events.PaintEvent;
-import org.eclipse.swt.events.PaintListener;
-import org.eclipse.swt.graphics.Cursor;
-import org.eclipse.swt.graphics.GC;
-import org.eclipse.swt.graphics.Image;
-import org.eclipse.swt.graphics.Rectangle;
-import org.eclipse.swt.widgets.Composite;
-import org.piccolo2d.PCamera;
-import org.piccolo2d.PComponent;
-import org.piccolo2d.PLayer;
-import org.piccolo2d.PRoot;
-import org.piccolo2d.event.PInputEventListener;
-import org.piccolo2d.event.PPanEventHandler;
-import org.piccolo2d.event.PZoomEventHandler;
-import org.piccolo2d.util.PBounds;
-import org.piccolo2d.util.PDebug;
-import org.piccolo2d.util.PPaintContext;
-import org.piccolo2d.util.PStack;
-
-
-/**
- * PSWTCanvas is an SWT Composite that can be used to embed
- * Piccolo into a SWT application. Canvases view the Piccolo scene graph
- * through a camera. The canvas manages screen updates coming from this camera,
- * and forwards swing mouse and keyboard events to the camera.
- *
- * @version 1.0
- * @author Jesse Grosjean
- */
-public class PSWTCanvas extends Composite implements PComponent {
- private static final int SWT_BUTTON1 = 1;
- private static final int SWT_BUTTON2 = 2;
- private static final int SWT_BUTTON3 = 3;
-
- /**
- * Terrible Singleton instance of the PSWTCanvas. Falsely assumes you will
- * only have one of these per application.
- */
- public static PSWTCanvas CURRENT_CANVAS = null;
-
- private Image backBuffer;
- private boolean doubleBuffered = true;
- private PCamera camera;
- private final PStack cursorStack;
- private Cursor curCursor;
- private int interacting;
- private int defaultRenderQuality;
- private int animatingRenderQuality;
- private int interactingRenderQuality;
- private final PPanEventHandler panEventHandler;
- private final PZoomEventHandler zoomEventHandler;
- private boolean paintingImmediately;
- private boolean animatingOnLastPaint;
-
- private boolean isButton1Pressed;
- private boolean isButton2Pressed;
- private boolean isButton3Pressed;
-
- /**
- * Construct a canvas with the basic scene graph consisting of a root,
- * camera, and layer. Event handlers for zooming and panning are
- * automatically installed.
- *
- * @param parent component onto which the canvas is installed
- * @param style component style for the PSWTCanvas
- */
- public PSWTCanvas(final Composite parent, final int style) {
- super(parent, style | SWT.NO_BACKGROUND | SWT.NO_REDRAW_RESIZE);
-
- CURRENT_CANVAS = this;
- cursorStack = new PStack();
- setCamera(createBasicSceneGraph());
- installInputSources();
- setDefaultRenderQuality(PPaintContext.HIGH_QUALITY_RENDERING);
- setAnimatingRenderQuality(PPaintContext.LOW_QUALITY_RENDERING);
- setInteractingRenderQuality(PPaintContext.LOW_QUALITY_RENDERING);
- panEventHandler = new PPanEventHandler();
- zoomEventHandler = new PZoomEventHandler();
- addInputEventListener(panEventHandler);
- addInputEventListener(zoomEventHandler);
-
- installPaintListener();
- installDisposeListener();
- }
-
- private void installPaintListener() {
- addPaintListener(new PaintListener() {
- public void paintControl(final PaintEvent pe) {
- paintComponent(pe.gc, pe.x, pe.y, pe.width, pe.height);
- }
- });
- }
-
- private void installDisposeListener() {
- SWTGraphics2D.incrementGCCount();
- addDisposeListener(new DisposeListener() {
- public void widgetDisposed(final DisposeEvent de) {
- getRoot().getActivityScheduler().removeAllActivities();
- SWTGraphics2D.decrementGCCount();
- }
- });
- }
-
- // ****************************************************************
- // Basic - Methods for accessing common Piccolo2D nodes.
- // ****************************************************************
-
- /**
- * Get the pan event handler associated with this canvas. This event handler
- * is set up to get events from the camera associated with this canvas by
- * default.
- *
- * @return the current pan event handler, which may be null
- */
- public PPanEventHandler getPanEventHandler() {
- return panEventHandler;
- }
-
- /**
- * Get the zoom event handler associated with this canvas. This event
- * handler is set up to get events from the camera associated with this
- * canvas by default.
- *
- * @return the event handler installed to handle zooming
- */
- public PZoomEventHandler getZoomEventHandler() {
- return zoomEventHandler;
- }
-
- /**
- * Return the camera associated with this canvas. All input events from this
- * canvas go through this camera. And this is the camera that paints this
- * canvas.
- *
- * @return the camera associated with this canvas
- */
- public PCamera getCamera() {
- return camera;
- }
-
- /**
- * Set the camera associated with this canvas. All input events from this
- * canvas go through this camera. And this is the camera that paints this
- * canvas.
- *
- * @param newCamera camera to attach to this canvas
- */
- public void setCamera(final PCamera newCamera) {
- if (camera != null) {
- camera.setComponent(null);
- }
-
- camera = newCamera;
-
- if (camera != null) {
- camera.setComponent(this);
-
- final Rectangle swtRect = getBounds();
-
- camera.setBounds(new Rectangle2D.Double(swtRect.x, swtRect.y, swtRect.width, swtRect.height));
- }
- }
-
- /**
- * Return root for this canvas.
- *
- * @return root of the scene this canvas is viewing through its camera
- */
- public PRoot getRoot() {
- return camera.getRoot();
- }
-
- /**
- * Helper method to return the first layer attached to the camera of this
- * canvas.
- *
- * Short form of canvas.getCamera.getLayer(0)
- *
- * @return the first layer attached to the camera of this canvas
- */
- public PLayer getLayer() {
- return camera.getLayer(0);
- }
-
- /**
- * Add an input listener to the camera associated with this canvas.
- *
- * @param listener listener to add to to the camera
- */
- public void addInputEventListener(final PInputEventListener listener) {
- getCamera().addInputEventListener(listener);
- }
-
- /**
- * Remove an input listener to the camera associated with this canvas. Does
- * nothign is the listener is not found.
- *
- * @param listener listener to remove from the set of event listeners
- * attached to this canvas.
- */
- public void removeInputEventListener(final PInputEventListener listener) {
- getCamera().removeInputEventListener(listener);
- }
-
- /**
- * Builds the basic scene graph associated with this canvas. Developers may
- * override this method to install their own layers, and cameras.
- *
- * @return PCamera viewing the freshly created scene
- */
- public PCamera createBasicSceneGraph() {
- final PRoot r = new PSWTRoot(this);
- final PLayer l = new PLayer();
- final PCamera c = new PCamera();
-
- r.addChild(c);
- r.addChild(l);
- c.addLayer(l);
-
- return c;
- }
-
- // ****************************************************************
- // Painting
- // ****************************************************************
-
- /**
- * Return true if this canvas has been marked as interacting. If so the
- * canvas will normally render at a lower quality that is faster.
- *
- * @return true if canvas is flagged as interacting
- */
- public boolean getInteracting() {
- return interacting > 0;
- }
-
- /**
- * Return true if any activities that respond with true to the method
- * isAnimating were run in the last PRoot.processInputs() loop. This values
- * is used by this canvas to determine the render quality to use for the
- * next paint.
- *
- * @return true if there is an animating activity that is currently active
- */
- public boolean getAnimating() {
- return getRoot().getActivityScheduler().getAnimating();
- }
-
- /**
- * Changes the number of callers that are interacting with the canvas. Will
- * allow the scene to be rendered in a lower quality if the number is not 0.
- *
- * @param isInteracting state the client considers the PSWTCanvas to be in
- * with regard to interacting
- */
- public void setInteracting(final boolean isInteracting) {
- if (isInteracting) {
- interacting++;
- }
- else {
- interacting--;
- }
-
- if (!getInteracting()) {
- repaint();
- }
- }
-
- /**
- * Get whether this canvas should use double buffering - the default is to
- * double buffer.
- *
- * @return true if double buffering is enabled
- */
- public boolean getDoubleBuffered() {
- return doubleBuffered;
- }
-
- /**
- * Set whether this canvas should use double buffering - the default is yes.
- *
- * @param doubleBuffered value of double buffering flas
- */
- public void setDoubleBuffered(final boolean doubleBuffered) {
- this.doubleBuffered = doubleBuffered;
- if (!doubleBuffered && backBuffer != null) {
- backBuffer.dispose();
- backBuffer = null;
- }
- }
-
- /**
- * Set the render quality that should be used when rendering this canvas.
- * The default value is PPaintContext.HIGH_QUALITY_RENDERING.
- *
- * @param requestedQuality supports PPaintContext.HIGH_QUALITY_RENDERING or
- * PPaintContext.LOW_QUALITY_RENDERING
- */
- public void setDefaultRenderQuality(final int requestedQuality) {
- defaultRenderQuality = requestedQuality;
- repaint();
- }
-
- /**
- * Set the render quality that should be used when rendering this canvas
- * when it is animating. The default value is
- * PPaintContext.LOW_QUALITY_RENDERING.
- *
- * @param requestedQuality supports PPaintContext.HIGH_QUALITY_RENDERING or
- * PPaintContext.LOW_QUALITY_RENDERING
- */
- public void setAnimatingRenderQuality(final int requestedQuality) {
- animatingRenderQuality = requestedQuality;
- repaint();
- }
-
- /**
- * Set the render quality that should be used when rendering this canvas
- * when it is interacting. The default value is
- * PPaintContext.LOW_QUALITY_RENDERING.
- *
- * @param requestedQuality supports PPaintContext.HIGH_QUALITY_RENDERING or
- * PPaintContext.LOW_QUALITY_RENDERING
- */
- public void setInteractingRenderQuality(final int requestedQuality) {
- interactingRenderQuality = requestedQuality;
- repaint();
- }
-
- /**
- * Set the canvas cursor, and remember the previous cursor on the cursor
- * stack. Under the hood it is mapping the java.awt.Cursor to
- * org.eclipse.swt.graphics.Cursor objects.
- *
- * @param newCursor new cursor to push onto the cursor stack
- */
- public void pushCursor(final java.awt.Cursor newCursor) {
- Cursor swtCursor = null;
- if (newCursor.getType() == java.awt.Cursor.N_RESIZE_CURSOR) {
- swtCursor = new Cursor(getDisplay(), SWT.CURSOR_SIZEN);
- }
- else if (newCursor.getType() == java.awt.Cursor.NE_RESIZE_CURSOR) {
- swtCursor = new Cursor(getDisplay(), SWT.CURSOR_SIZENE);
- }
- else if (newCursor.getType() == java.awt.Cursor.NW_RESIZE_CURSOR) {
- swtCursor = new Cursor(getDisplay(), SWT.CURSOR_SIZENW);
- }
- else if (newCursor.getType() == java.awt.Cursor.S_RESIZE_CURSOR) {
- swtCursor = new Cursor(getDisplay(), SWT.CURSOR_SIZES);
- }
- else if (newCursor.getType() == java.awt.Cursor.SE_RESIZE_CURSOR) {
- swtCursor = new Cursor(getDisplay(), SWT.CURSOR_SIZESE);
- }
- else if (newCursor.getType() == java.awt.Cursor.SW_RESIZE_CURSOR) {
- swtCursor = new Cursor(getDisplay(), SWT.CURSOR_SIZESW);
- }
- else if (newCursor.getType() == java.awt.Cursor.E_RESIZE_CURSOR) {
- swtCursor = new Cursor(getDisplay(), SWT.CURSOR_SIZEE);
- }
- else if (newCursor.getType() == java.awt.Cursor.W_RESIZE_CURSOR) {
- swtCursor = new Cursor(getDisplay(), SWT.CURSOR_SIZEW);
- }
- else if (newCursor.getType() == java.awt.Cursor.TEXT_CURSOR) {
- swtCursor = new Cursor(getDisplay(), SWT.CURSOR_IBEAM);
- }
- else if (newCursor.getType() == java.awt.Cursor.HAND_CURSOR) {
- swtCursor = new Cursor(getDisplay(), SWT.CURSOR_HAND);
- }
- else if (newCursor.getType() == java.awt.Cursor.MOVE_CURSOR) {
- swtCursor = new Cursor(getDisplay(), SWT.CURSOR_SIZEALL);
- }
- else if (newCursor.getType() == java.awt.Cursor.CROSSHAIR_CURSOR) {
- swtCursor = new Cursor(getDisplay(), SWT.CURSOR_CROSS);
- }
- else if (newCursor.getType() == java.awt.Cursor.WAIT_CURSOR) {
- swtCursor = new Cursor(getDisplay(), SWT.CURSOR_WAIT);
- }
-
- if (swtCursor != null) {
- if (curCursor != null) {
- cursorStack.push(curCursor);
- }
- curCursor = swtCursor;
- setCursor(swtCursor);
- }
- }
-
- /**
- * Pop the cursor on top of the cursorStack and set it as the canvas cursor.
- */
- public void popCursor() {
- if (curCursor != null) {
- // We must manually dispose of cursors under SWT
- curCursor.dispose();
- }
-
- if (cursorStack.isEmpty()) {
- curCursor = null;
- }
- else {
- curCursor = (Cursor) cursorStack.pop();
- }
-
- // This sets the cursor back to default
- setCursor(curCursor);
- }
-
- // ****************************************************************
- // Code to manage connection to Swing. There appears to be a bug in
- // swing where it will occasionally send to many mouse pressed or mouse
- // released events. Below we attempt to filter out those cases before
- // they get delivered to the Piccolo2D framework.
- // ****************************************************************
-
- /**
- * This method installs mouse and key listeners on the canvas that forward
- * those events to Piccolo2D.
- */
- protected void installInputSources() {
- MouseInputSource mouseInputSource = new MouseInputSource();
- addMouseListener(mouseInputSource);
- addMouseMoveListener(mouseInputSource);
-
- addKeyListener(new KeyboardInputSource());
- }
-
- /**
- * Dispatches the given event to the default input manager for the root of
- * this canvas.
- *
- * @param awtEvent awt event needing dispatching
- * @param type type of the event
- */
- protected void sendInputEventToInputManager(final InputEvent awtEvent, final int type) {
- getRoot().getDefaultInputManager().processEventFromCamera(awtEvent, type, getCamera());
- }
-
- /**
- * Changes the bounds of this PSWTCanvas. Updating the camera and the double
- * buffered image appropriately.
- *
- * @param x left of the new bounds
- * @param y top of the new bounds
- * @param newWidth new width of the bounds
- * @param newHeight new height of the bounds
- */
- public void setBounds(final int x, final int y, final int newWidth, final int newHeight) {
- camera.setBounds(camera.getX(), camera.getY(), newWidth, newHeight);
-
- if (backBufferNeedsResizing(newWidth, newHeight)) {
- resizeBackBuffer(newWidth, newHeight);
- }
-
- super.setBounds(x, y, newWidth, newHeight);
- }
-
- private void resizeBackBuffer(final int newWidth, final int newHeight) {
- if (backBuffer != null) {
- backBuffer.dispose();
- }
- backBuffer = new Image(getDisplay(), newWidth, newHeight);
- }
-
- private boolean backBufferNeedsResizing(final int newWidth, final int newHeight) {
- if (!doubleBuffered) {
- return false;
- }
-
- if (backBuffer == null) {
- return true;
- }
-
- return backBuffer.getBounds().width < newWidth || backBuffer.getBounds().height < newHeight;
- }
-
- /**
- * Exists to dispatch from the Swing's repaint method to SWT's redraw
- * method.
- */
- public void repaint() {
- super.redraw();
- }
-
- /**
- * Flags the bounds provided as needing to be redrawn.
- *
- * @param bounds the bounds that should be repainted
- */
- public void repaint(final PBounds bounds) {
- bounds.expandNearestIntegerDimensions();
- bounds.inset(-1, -1);
-
- redraw((int) bounds.x, (int) bounds.y, (int) bounds.width, (int) bounds.height, true);
- }
-
- /**
- * Paints the region specified of the canvas onto the given Graphics
- * Context.
- *
- * @param gc graphics onto within painting should occur
- * @param x left of the dirty region
- * @param y top of the dirty region
- * @param w width of the dirty region
- * @param h height of the dirty region
- */
- public void paintComponent(final GC gc, final int x, final int y, final int w, final int h) {
- PDebug.startProcessingOutput();
-
- GC imageGC = null;
- Graphics2D g2 = null;
- if (doubleBuffered) {
- imageGC = new GC(backBuffer);
- g2 = new SWTGraphics2D(imageGC, getDisplay());
- }
- else {
- g2 = new SWTGraphics2D(gc, getDisplay());
- }
-
- g2.setColor(Color.white);
- g2.setBackground(Color.white);
-
- final Rectangle rect = getBounds();
- g2.fillRect(0, 0, rect.width, rect.height);
-
- // This fixes a problem with standard debugging of region management in
- // SWT
- if (PDebug.debugRegionManagement) {
- final Rectangle r = gc.getClipping();
- final Rectangle2D r2 = new Rectangle2D.Double(r.x, r.y, r.width, r.height);
- g2.setBackground(PDebug.getDebugPaintColor());
- g2.fill(r2);
- }
-
- // create new paint context and set render quality
- final PPaintContext paintContext = new PPaintContext(g2);
- if (getInteracting() || getAnimating()) {
- if (interactingRenderQuality > animatingRenderQuality) {
- paintContext.setRenderQuality(interactingRenderQuality);
- }
- else {
- paintContext.setRenderQuality(animatingRenderQuality);
- }
- }
- else {
- paintContext.setRenderQuality(defaultRenderQuality);
- }
-
- // paint Piccolo2D
- camera.fullPaint(paintContext);
-
- // if switched state from animating to not animating invalidate
- // the entire screen so that it will be drawn with the default instead
- // of animating render quality.
- if (animatingOnLastPaint && !getAnimating()) {
- repaint();
- }
- animatingOnLastPaint = getAnimating();
-
- final boolean region = PDebug.debugRegionManagement;
- PDebug.debugRegionManagement = false;
- PDebug.endProcessingOutput(g2);
- PDebug.debugRegionManagement = region;
-
- if (doubleBuffered) {
- gc.drawImage(backBuffer, 0, 0);
-
- // Dispose of the allocated image gc
- imageGC.dispose();
- }
- }
-
- /**
- * Performs an immediate repaint if no other client is currently performing
- * one.
- */
- public void paintImmediately() {
- if (paintingImmediately) {
- return;
- }
-
- paintingImmediately = true;
- redraw();
- update();
- paintingImmediately = false;
- }
-
- private final class KeyboardInputSource implements KeyListener {
- public void keyPressed(final KeyEvent ke) {
- final java.awt.event.KeyEvent inputEvent = new PSWTKeyEvent(ke, java.awt.event.KeyEvent.KEY_PRESSED);
- sendInputEventToInputManager(inputEvent, java.awt.event.KeyEvent.KEY_PRESSED);
- }
-
- public void keyReleased(final KeyEvent ke) {
- final java.awt.event.KeyEvent inputEvent = new PSWTKeyEvent(ke, java.awt.event.KeyEvent.KEY_RELEASED);
- sendInputEventToInputManager(inputEvent, java.awt.event.KeyEvent.KEY_RELEASED);
- }
- }
-
- private final class MouseInputSource implements MouseListener, MouseMoveListener {
- public void mouseMove(final MouseEvent me) {
- if (isButton1Pressed || isButton2Pressed || isButton3Pressed) {
- final java.awt.event.MouseEvent inputEvent = new PSWTMouseEvent(me,
- java.awt.event.MouseEvent.MOUSE_DRAGGED, 1);
- sendInputEventToInputManager(inputEvent, java.awt.event.MouseEvent.MOUSE_DRAGGED);
- }
- else {
- final java.awt.event.MouseEvent inputEvent = new PSWTMouseEvent(me,
- java.awt.event.MouseEvent.MOUSE_MOVED, 1);
- sendInputEventToInputManager(inputEvent, java.awt.event.MouseEvent.MOUSE_MOVED);
- }
- }
-
- public void mouseDown(final MouseEvent mouseEvent) {
- boolean shouldBalanceEvent = false;
-
- switch (mouseEvent.button) {
- case SWT_BUTTON1:
- if (isButton1Pressed) {
- shouldBalanceEvent = true;
- }
- isButton1Pressed = true;
- break;
- case SWT_BUTTON2:
- if (isButton2Pressed) {
- shouldBalanceEvent = true;
- }
- isButton2Pressed = true;
- break;
- case SWT_BUTTON3:
- if (isButton3Pressed) {
- shouldBalanceEvent = true;
- }
- isButton3Pressed = true;
- break;
- default:
- }
-
- if (shouldBalanceEvent) {
- final java.awt.event.MouseEvent balanceEvent = new PSWTMouseEvent(mouseEvent,
- java.awt.event.MouseEvent.MOUSE_RELEASED, 1);
- sendInputEventToInputManager(balanceEvent, java.awt.event.MouseEvent.MOUSE_RELEASED);
- }
-
- final java.awt.event.MouseEvent balanceEvent = new PSWTMouseEvent(mouseEvent,
- java.awt.event.MouseEvent.MOUSE_PRESSED, 1);
- sendInputEventToInputManager(balanceEvent, java.awt.event.MouseEvent.MOUSE_PRESSED);
- }
-
- public void mouseUp(final MouseEvent me) {
- boolean shouldBalanceEvent = false;
-
- switch (me.button) {
- case SWT_BUTTON1:
- if (!isButton1Pressed) {
- shouldBalanceEvent = true;
- }
- isButton1Pressed = false;
- break;
- case SWT_BUTTON2:
- if (!isButton2Pressed) {
- shouldBalanceEvent = true;
- }
- isButton2Pressed = false;
- break;
- case SWT_BUTTON3:
- if (!isButton3Pressed) {
- shouldBalanceEvent = true;
- }
- isButton3Pressed = false;
- break;
- default:
- }
-
- if (shouldBalanceEvent) {
- final java.awt.event.MouseEvent balanceEvent = new PSWTMouseEvent(me,
- java.awt.event.MouseEvent.MOUSE_PRESSED, 1);
- sendInputEventToInputManager(balanceEvent, java.awt.event.MouseEvent.MOUSE_PRESSED);
- }
-
- final java.awt.event.MouseEvent balanceEvent = new PSWTMouseEvent(me,
- java.awt.event.MouseEvent.MOUSE_RELEASED, 1);
- sendInputEventToInputManager(balanceEvent, java.awt.event.MouseEvent.MOUSE_RELEASED);
- }
-
- public void mouseDoubleClick(final MouseEvent me) {
- // This doesn't work with click event types for some reason - it
- // has to do with how the click and release events are ordered,
- // I think
- java.awt.event.MouseEvent inputEvent = new PSWTMouseEvent(me, java.awt.event.MouseEvent.MOUSE_PRESSED, 2);
- sendInputEventToInputManager(inputEvent, java.awt.event.MouseEvent.MOUSE_PRESSED);
- inputEvent = new PSWTMouseEvent(me, java.awt.event.MouseEvent.MOUSE_RELEASED, 2);
- sendInputEventToInputManager(inputEvent, java.awt.event.MouseEvent.MOUSE_RELEASED);
- }
- }
-}
diff --git a/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTHandle.java b/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTHandle.java
deleted file mode 100644
index dff03da..0000000
--- a/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTHandle.java
+++ /dev/null
@@ -1,257 +0,0 @@
-/*
- * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org
- * Copyright (c) 1998-2008, University of Maryland
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification, are permitted provided
- * that the following conditions are met:
- *
- * Redistributions of source code must retain the above copyright notice, this list of conditions
- * and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
- * and the following disclaimer in the documentation and/or other materials provided with the
- * distribution.
- *
- * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its
- * contributors may be used to endorse or promote products derived from this software without specific
- * prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
- * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
- * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package edu.umd.cs.piccolox.swt;
-
-import java.awt.Color;
-import java.awt.Shape;
-import java.awt.event.InputEvent;
-import java.awt.geom.Ellipse2D;
-import java.awt.geom.Point2D;
-import java.beans.PropertyChangeEvent;
-import java.beans.PropertyChangeListener;
-import java.io.IOException;
-import java.io.ObjectInputStream;
-
-import org.piccolo2d.PCamera;
-import org.piccolo2d.PNode;
-import org.piccolo2d.event.PDragSequenceEventHandler;
-import org.piccolo2d.event.PInputEvent;
-import org.piccolo2d.event.PInputEventFilter;
-import org.piccolo2d.extras.util.PLocator;
-import org.piccolo2d.extras.util.PNodeLocator;
-import org.piccolo2d.util.PBounds;
-import org.piccolo2d.util.PDimension;
-
-
-/**
- * PSWTHandle is used to modify some aspect of Piccolo when it is dragged.
- * Each handle has a PLocator that it uses to automatically position itself. See
- * PSWTBoundsHandle for an example of a handle that resizes the bounds of another
- * node.
- *
- * - * @version 1.0 - * @author Jesse Grosjean - */ -public class PSWTHandle extends PSWTPath { - private static final long serialVersionUID = 1L; - /** The Default Size of a handle not including its border. */ - public static final float DEFAULT_HANDLE_SIZE = 8; - /** The default shape to use when drawing handles. Default is an ellipse. */ - public static final Shape DEFAULT_HANDLE_SHAPE = new Ellipse2D.Float(0f, 0f, DEFAULT_HANDLE_SIZE, DEFAULT_HANDLE_SIZE); - /** The default color to use when drawing a handle. (white) */ - public static final Color DEFAULT_COLOR = Color.white; - - private PLocator locator; - private PDragSequenceEventHandler handleDragger; - - /** - * Construct a new handle that will use the given locator to locate itself - * on its parent node. - * - * @param aLocator locator to use when positioning this handle - */ - public PSWTHandle(final PLocator aLocator) { - super(DEFAULT_HANDLE_SHAPE); - locator = aLocator; - setPaint(DEFAULT_COLOR); - installHandleEventHandlers(); - } - - /** - * Installs the handler that will reposition the handle when it is dragged, - * and invoke appropriate call backs. - */ - protected void installHandleEventHandlers() { - handleDragger = new HandleDragHandler(); - - addPropertyChangeListener(PNode.PROPERTY_TRANSFORM, new PropertyChangeListener() { - public void propertyChange(final PropertyChangeEvent evt) { - relocateHandle(); - } - }); - - // so reject them so we don't consume them - addInputEventListener(handleDragger); - } - - /** - * Return the event handler that is responsible for the drag handle - * interaction. - * - * @return handler responsible for responding to drag events - */ - public PDragSequenceEventHandler getHandleDraggerHandler() { - return handleDragger; - } - - /** - * Get the locator that this handle uses to position itself on its parent - * node. - * - * @return locator used to position this handle - */ - public PLocator getLocator() { - return locator; - } - - /** - * Set the locator that this handle uses to position itself on its parent - * node. - * - * @param aLocator used to position this handle - */ - public void setLocator(final PLocator aLocator) { - locator = aLocator; - invalidatePaint(); - relocateHandle(); - } - - // **************************************************************** - // Handle Dragging - These are the methods the subclasses should - // normally override to give a handle unique behavior. - // **************************************************************** - - /** - * Override this method to get notified when the handle starts to get - * dragged. - * - * @param aLocalPoint point at which dragging was started relative to the - * handle's coordinate system - * @param aEvent event representing the start of the drag - */ - public void startHandleDrag(final Point2D aLocalPoint, final PInputEvent aEvent) { - } - - /** - * Override this method to get notified as the handle is dragged. - * - * @param aLocalDimension magnitude of the dragHandle event in the - * dimensions of the handle's coordinate system. - * @param aEvent event representing the drag - */ - public void dragHandle(final PDimension aLocalDimension, final PInputEvent aEvent) { - } - - /** - * Override this method to get notified when the handle stops getting - * dragged. - * - * @param aLocalPoint point at which dragging was ended relative to the - * handle's coordinate system - * @param aEvent event representing the end of the drag - */ - public void endHandleDrag(final Point2D aLocalPoint, final PInputEvent aEvent) { - } - - // **************************************************************** - // Layout - When a handle's parent's layout changes the handle - // invalidates its own layout and then repositions itself on its - // parents bounds using its locator to determine that new - // position. - // **************************************************************** - - /** {@inheritDoc} */ - public void setParent(final PNode newParent) { - super.setParent(newParent); - relocateHandle(); - } - - /** {@inheritDoc} */ - public void parentBoundsChanged() { - relocateHandle(); - } - - /** - * Force this handle to relocate itself using its locator. - */ - public void relocateHandle() { - if (locator != null) { - final PBounds b = getBoundsReference(); - final Point2D aPoint = locator.locatePoint(null); - - if (locator instanceof PNodeLocator) { - final PNode located = ((PNodeLocator) locator).getNode(); - final PNode parent = getParent(); - - located.localToGlobal(aPoint); - globalToLocal(aPoint); - - if (parent != located && parent instanceof PCamera) { - ((PCamera) parent).viewToLocal(aPoint); - } - } - - final double newCenterX = aPoint.getX(); - final double newCenterY = aPoint.getY(); - - if (newCenterX != b.getCenterX() || newCenterY != b.getCenterY()) { - centerBoundsOnPoint(newCenterX, newCenterY); - } - } - } - - // **************************************************************** - // Serialization - // **************************************************************** - - private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { - in.defaultReadObject(); - installHandleEventHandlers(); - } - - private final class HandleDragHandler extends PDragSequenceEventHandler { - public HandleDragHandler() { - final PInputEventFilter filter = new PInputEventFilter(InputEvent.BUTTON1_MASK); - setEventFilter(filter); - filter.setMarksAcceptedEventsAsHandled(true); - filter.setAcceptsMouseEntered(false); - filter.setAcceptsMouseExited(false); - filter.setAcceptsMouseMoved(false); - } - - protected void startDrag(final PInputEvent event) { - super.startDrag(event); - startHandleDrag(event.getPositionRelativeTo(PSWTHandle.this), event); - } - - protected void drag(final PInputEvent event) { - super.drag(event); - final PDimension aDelta = event.getDeltaRelativeTo(PSWTHandle.this); - if (aDelta.getWidth() != 0 || aDelta.getHeight() != 0) { - dragHandle(aDelta, event); - } - } - - protected void endDrag(final PInputEvent event) { - super.endDrag(event); - endHandleDrag(event.getPositionRelativeTo(PSWTHandle.this), event); - } - } -} \ No newline at end of file diff --git a/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTImage.java b/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTImage.java deleted file mode 100644 index b977eb0..0000000 --- a/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTImage.java +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.swt; - -import org.eclipse.swt.events.DisposeEvent; -import org.eclipse.swt.events.DisposeListener; -import org.eclipse.swt.graphics.Image; -import org.eclipse.swt.graphics.Rectangle; -import org.piccolo2d.PNode; -import org.piccolo2d.nodes.PImage; -import org.piccolo2d.util.PBounds; -import org.piccolo2d.util.PPaintContext; - - -/** - * PSWTImage is a wrapper around a org.eclipse.swt.graphics.Image. - * - * @version 1.0 - * @author Jesse Grosjean - */ -public class PSWTImage extends PNode { - private static final long serialVersionUID = 1L; - - private final transient PSWTCanvas canvas; - - private transient Image image; - - /** - * Constructs a PSWTImage attached to the provided canvas and with a null - * image. - * - * The developer will need to call setImage for this node to be useful. - * - * TODO: determine if canvas is actually necessary - * - * @param canvas canvas to associate with the image node - */ - public PSWTImage(final PSWTCanvas canvas) { - this.canvas = canvas; - canvas.addDisposeListener(new DisposeListener() { - public void widgetDisposed(final DisposeEvent de) { - disposeImage(); - } - }); - } - - /** - * Constructs a PSWTImage wrapping the provided image. - * - * @param canvas canvas to associate with the image node - * @param image image to be wrapped by this PSWTImage - */ - public PSWTImage(final PSWTCanvas canvas, final Image image) { - this(canvas); - setImage(image); - } - - /** - * Constructs a PSWTImage wrapping the provided image after loading it from - * the file. - * - * @param canvas canvas to associate with the image node - * @param fileName path to the image, will be loaded and converted to an - * Image internally - */ - public PSWTImage(final PSWTCanvas canvas, final String fileName) { - this(canvas); - setImage(fileName); - } - - /** - * Returns the image that is shown by this node, may be null. - * - * @return the image that is shown by this node - */ - public Image getImage() { - return image; - } - - /** - * Set the image that is wrapped by this PImage node. This method will also - * load the image using a MediaTracker before returning. And if the this - * PImage is accelerated that image will be copied into an accelerated image - * if needed. Note that this may cause undesired results with images that - * have transparent regions, for those cases you may want to set the PImage - * to be not accelerated. - * - * @param filePath path to the file to load as an image - */ - public void setImage(final String filePath) { - setImage(new Image(canvas.getDisplay(), filePath)); - } - - /** - * Set the image that is wrapped by this PImage node. This method will also - * load the image using a MediaTracker before returning. And if the this - * PImage is accelerated that I'm will be copied into an accelerated image - * if needed. Note that this may cause undesired results with images that - * have transparent regions, for those cases you may want to set the PImage - * to be not accelerated. - * - * @param newImage the image that should replace the current one - */ - public void setImage(final Image newImage) { - final Image old = image; - image = newImage; - - if (image != null) { - final Rectangle bounds = getImage().getBounds(); - setBounds(0, 0, bounds.width, bounds.height); - invalidatePaint(); - } - - firePropertyChange(PImage.PROPERTY_CODE_IMAGE, PImage.PROPERTY_IMAGE, old, image); - } - - /** - * Subclasses may override this method to provide different image dispose - * behavior. - */ - protected void disposeImage() { - if (image != null) { - image.dispose(); - } - } - - /** {@inheritDoc} */ - protected void paint(final PPaintContext paintContext) { - if (getImage() != null) { - final Rectangle r = image.getBounds(); - final PBounds b = getBoundsReference(); - final SWTGraphics2D g2 = (SWTGraphics2D) paintContext.getGraphics(); - - if (b.x == 0 && b.y == 0 && b.width == r.width && b.height == r.height) { - g2.drawImage(image, 0, 0); - } - else { - g2.translate(b.x, b.y); - g2.scale(b.width / r.width, b.height / r.height); - g2.drawImage(image, 0, 0); - g2.scale(r.width / b.width, r.height / b.height); - g2.translate(-b.x, -b.y); - } - } - } -} diff --git a/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTKeyEvent.java b/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTKeyEvent.java deleted file mode 100644 index eea863d..0000000 --- a/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTKeyEvent.java +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.swt; - -import java.awt.Component; -import java.awt.event.InputEvent; -import java.awt.event.KeyEvent; - -import org.eclipse.swt.SWT; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Widget; - -/** - * Key event overridden to wrap an SWT KeyEvent as a swing KeyEvent. - * - * @author Lance Good - */ -public class PSWTKeyEvent extends KeyEvent { - private static final long serialVersionUID = 1L; - - private static Component fakeSrc = new Component() { - }; - - private org.eclipse.swt.events.KeyEvent swtEvent; - - /** - * Creates an object that wraps a SWT Key event. Making it queriable from - * Piccolo2d as though it were a Swing one. - * - * @param ke key event object - * @param eventType type of key event - */ - public PSWTKeyEvent(final org.eclipse.swt.events.KeyEvent ke, final int eventType) { - super(fakeSrc, eventType, ke.time, 0, ke.keyCode, ke.character, KeyEvent.KEY_LOCATION_STANDARD); - - swtEvent = ke; - } - - /** {@inheritDoc} */ - public Object getSource() { - return swtEvent.getSource(); - } - - /** {@inheritDoc} */ - public boolean isShiftDown() { - return (swtEvent.stateMask & SWT.SHIFT) != 0; - } - - /** {@inheritDoc} */ - public boolean isControlDown() { - return (swtEvent.stateMask & SWT.CONTROL) != 0; - } - - /** {@inheritDoc} */ - public boolean isAltDown() { - return (swtEvent.stateMask & SWT.ALT) != 0; - } - - /** {@inheritDoc} */ - public int getModifiers() { - int modifiers = 0; - - if (swtEvent != null) { - if ((swtEvent.stateMask & SWT.ALT) != 0) { - modifiers = modifiers | InputEvent.ALT_MASK; - } - if ((swtEvent.stateMask & SWT.CONTROL) != 0) { - modifiers = modifiers | InputEvent.CTRL_MASK; - } - if ((swtEvent.stateMask & SWT.SHIFT) != 0) { - modifiers = modifiers | InputEvent.SHIFT_MASK; - } - } - - return modifiers; - } - - /** {@inheritDoc} */ - public int getModifiersEx() { - int modifiers = 0; - - if (swtEvent != null) { - if ((swtEvent.stateMask & SWT.ALT) != 0) { - modifiers = modifiers | InputEvent.ALT_DOWN_MASK; - } - if ((swtEvent.stateMask & SWT.CONTROL) != 0) { - modifiers = modifiers | InputEvent.CTRL_DOWN_MASK; - } - if ((swtEvent.stateMask & SWT.SHIFT) != 0) { - modifiers = modifiers | InputEvent.SHIFT_DOWN_MASK; - } - } - - return modifiers; - } - - /** {@inheritDoc} */ - public boolean isActionKey() { - return false; - } - - /** - * Returns the widget from which the event was emitted. - * - * @return source widget - */ - public Widget getWidget() { - return swtEvent.widget; - } - - /** - * Return the display on which the interaction occurred. - * - * @return display on which the interaction occurred - */ - public Display getDisplay() { - return swtEvent.display; - } - - /** - * Return the associated SWT data for the event. - * - * @return data associated to the SWT event - */ - public Object getData() { - return swtEvent.data; - } -} diff --git a/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTMouseEvent.java b/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTMouseEvent.java deleted file mode 100644 index 0a40f77..0000000 --- a/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTMouseEvent.java +++ /dev/null @@ -1,196 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.swt; - -import java.awt.Component; -import java.awt.event.InputEvent; -import java.awt.event.MouseEvent; - -import org.eclipse.swt.SWT; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Widget; - -/** - * Mouse event overridden to wrap an SWT MouseEvent as a Swing MouseEvent. - * - * @author Lance Good - */ -public class PSWTMouseEvent extends MouseEvent { - private static final int SWT_BUTTON1 = 1; - private static final int SWT_BUTTON2 = 2; - private static final int SWT_BUTTON3 = 3; - - private static final long serialVersionUID = 1L; - - private static Component fakeSrc = new Component() { - }; - - /** Event being wrapped. */ - protected org.eclipse.swt.events.MouseEvent swtEvent; - - /** Number times the mouse was clicked in relation to the wrapped event. */ - protected int clickCount; - - /** - * Constructs a PSWTMouseEvent that wraps the provided SWT MouseEvent as a - * Swing one. - * - * @param me Mouse Event being wrapped - * @param type event type - * @param clickCount number of times the mouse has been clicked - */ - public PSWTMouseEvent(final org.eclipse.swt.events.MouseEvent me, final int type, final int clickCount) { - super(fakeSrc, type, me.time, 0, me.x, me.y, clickCount, me.button == SWT_BUTTON3, me.button); - - swtEvent = me; - this.clickCount = clickCount; - } - - /** {@inheritDoc} */ - public Object getSource() { - return swtEvent.getSource(); - } - - /** {@inheritDoc} */ - public int getClickCount() { - return clickCount; - } - - /** {@inheritDoc} */ - public int getButton() { - switch (swtEvent.button) { - case SWT_BUTTON1: - return MouseEvent.BUTTON1; - case SWT_BUTTON2: - return MouseEvent.BUTTON2; - case SWT_BUTTON3: - return MouseEvent.BUTTON3; - default: - return MouseEvent.NOBUTTON; - } - } - - /** {@inheritDoc} */ - public boolean isShiftDown() { - return (swtEvent.stateMask & SWT.SHIFT) != 0; - } - - /** {@inheritDoc} */ - public boolean isControlDown() { - return (swtEvent.stateMask & SWT.CONTROL) != 0; - } - - /** {@inheritDoc} */ - public boolean isAltDown() { - return (swtEvent.stateMask & SWT.ALT) != 0; - } - - /** {@inheritDoc} */ - public int getModifiers() { - int modifiers = 0; - - if (swtEvent != null) { - if ((swtEvent.stateMask & SWT.ALT) != 0) { - modifiers = modifiers | InputEvent.ALT_MASK; - } - if ((swtEvent.stateMask & SWT.CONTROL) != 0) { - modifiers = modifiers | InputEvent.CTRL_MASK; - } - if ((swtEvent.stateMask & SWT.SHIFT) != 0) { - modifiers = modifiers | InputEvent.SHIFT_MASK; - } - if (swtEvent.button == SWT_BUTTON1 || (swtEvent.stateMask & SWT.BUTTON1) != 0) { - modifiers = modifiers | InputEvent.BUTTON1_MASK; - } - if (swtEvent.button == SWT_BUTTON2 || (swtEvent.stateMask & SWT.BUTTON2) != 0) { - modifiers = modifiers | InputEvent.BUTTON2_MASK; - } - if (swtEvent.button == SWT_BUTTON3 || (swtEvent.stateMask & SWT.BUTTON3) != 0) { - modifiers = modifiers | InputEvent.BUTTON3_MASK; - } - } - - return modifiers; - } - - /** {@inheritDoc} */ - public int getModifiersEx() { - int modifiers = 0; - - if (swtEvent != null) { - if ((swtEvent.stateMask & SWT.ALT) != 0) { - modifiers = modifiers | InputEvent.ALT_DOWN_MASK; - } - if ((swtEvent.stateMask & SWT.CONTROL) != 0) { - modifiers = modifiers | InputEvent.CTRL_DOWN_MASK; - } - if ((swtEvent.stateMask & SWT.SHIFT) != 0) { - modifiers = modifiers | InputEvent.SHIFT_DOWN_MASK; - } - if (swtEvent.button == SWT_BUTTON1 || (swtEvent.stateMask & SWT.BUTTON1) != 0) { - modifiers = modifiers | InputEvent.BUTTON1_DOWN_MASK; - } - if (swtEvent.button == SWT_BUTTON2 || (swtEvent.stateMask & SWT.BUTTON2) != 0) { - modifiers = modifiers | InputEvent.BUTTON2_DOWN_MASK; - } - if (swtEvent.button == SWT_BUTTON3 || (swtEvent.stateMask & SWT.BUTTON3) != 0) { - modifiers = modifiers | InputEvent.BUTTON3_DOWN_MASK; - } - } - - return modifiers; - } - - /** - * Returns the widget from which the event was emitted. - * - * @return source widget - */ - public Widget getWidget() { - return swtEvent.widget; - } - - /** - * Return the display on which the interaction occurred. - * - * @return display on which the interaction occurred - */ - public Display getDisplay() { - return swtEvent.display; - } - - /** - * Return the associated SWT data for the event. - * - * @return data associated to the SWT event - */ - public Object getData() { - return swtEvent.data; - } -} diff --git a/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTPath.java b/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTPath.java deleted file mode 100644 index 27cf470..0000000 --- a/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTPath.java +++ /dev/null @@ -1,574 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.swt; - -import java.awt.BasicStroke; -import java.awt.Color; -import java.awt.Paint; -import java.awt.Shape; -import java.awt.geom.AffineTransform; -import java.awt.geom.Arc2D; -import java.awt.geom.Ellipse2D; -import java.awt.geom.GeneralPath; -import java.awt.geom.Line2D; -import java.awt.geom.Point2D; -import java.awt.geom.Rectangle2D; -import java.awt.geom.RoundRectangle2D; - -import org.piccolo2d.PNode; -import org.piccolo2d.nodes.PPath; -import org.piccolo2d.util.PAffineTransform; -import org.piccolo2d.util.PAffineTransformException; -import org.piccolo2d.util.PBounds; -import org.piccolo2d.util.PPaintContext; - - -/** - * PSWTPath is a wrapper around a java.awt.geom.GeneralPath, with - * workarounds for drawing shapes in SWT where necessary. - * - * @version 1.0 - * @author Jesse Grosjean - */ -public class PSWTPath extends PNode { - private static final long serialVersionUID = 1L; - - /** - * The property name that identifies a change of this node's path. In any - * property change event the new value will be a reference to this node's - * path, but old value will always be null. - */ - public static final String PROPERTY_SHAPE = "shape"; - - private static final double BOUNDS_TOLERANCE = 0.01; - private static final Rectangle2D.Float TEMP_RECTANGLE = new Rectangle2D.Float(); - private static final RoundRectangle2D.Float TEMP_ROUNDRECTANGLE = new RoundRectangle2D.Float(); - private static final Ellipse2D.Float TEMP_ELLIPSE = new Ellipse2D.Float(); - private static final Color DEFAULT_STROKE_PAINT = Color.black; - private static final BasicStroke BASIC_STROKE = new BasicStroke(); - private static final float PEN_WIDTH = 1f; - - private Paint strokePaint; - - private boolean updatingBoundsFromPath; - private Shape origShape; - private Shape shape; - - private PAffineTransform internalXForm; - private AffineTransform inverseXForm; - - private double[] shapePts; - - /** - * Creates a path representing the rectangle provided. - * - * @param x left of rectangle - * @param y top of rectangle - * @param width width of rectangle - * @param height height of rectangle - * @return created rectangle - */ - public static PSWTPath createRectangle(final float x, final float y, final float width, final float height) { - TEMP_RECTANGLE.setFrame(x, y, width, height); - final PSWTPath result = new PSWTPath(TEMP_RECTANGLE); - result.setPaint(Color.white); - return result; - } - - /** - * Creates a path representing the rounded rectangle provided. - * - * @param x left of rectangle - * @param y top of rectangle - * @param width width of rectangle - * @param height height of rectangle - * @param arcWidth width of the arc at the corners - * @param arcHeight height of arc at the corners - * @return created rounded rectangle - */ - public static PSWTPath createRoundRectangle(final float x, final float y, final float width, final float height, - final float arcWidth, final float arcHeight) { - TEMP_ROUNDRECTANGLE.setRoundRect(x, y, width, height, arcWidth, arcHeight); - final PSWTPath result = new PSWTPath(TEMP_ROUNDRECTANGLE); - result.setPaint(Color.white); - return result; - } - - /** - * Creates a path representing an ellipse that covers the rectangle - * provided. - * - * @param x left of rectangle - * @param y top of rectangle - * @param width width of rectangle - * @param height height of rectangle - * @return created ellipse - */ - public static PSWTPath createEllipse(final float x, final float y, final float width, final float height) { - TEMP_ELLIPSE.setFrame(x, y, width, height); - final PSWTPath result = new PSWTPath(TEMP_ELLIPSE); - result.setPaint(Color.white); - return result; - } - - /** - * Creates a PPath for the poly-line for the given points. - * - * @param points array of points for the point lines - * - * @return created poly-line for the given points - */ - public static PSWTPath createPolyline(final Point2D[] points) { - final PSWTPath result = new PSWTPath(); - result.setPathToPolyline(points); - result.setPaint(Color.white); - return result; - } - - /** - * Creates a PPath for the poly-line for the given points. - * - * @param xp array of x components of the points of the poly-lines - * @param yp array of y components of the points of the poly-lines - * - * @return created poly-line for the given points - */ - public static PSWTPath createPolyline(final float[] xp, final float[] yp) { - final PSWTPath result = new PSWTPath(); - result.setPathToPolyline(xp, yp); - result.setPaint(Color.white); - return result; - } - - /** - * Creates an empty PSWTPath. - */ - public PSWTPath() { - strokePaint = DEFAULT_STROKE_PAINT; - } - - /** - * Creates an SWTPath in the given shape with the default paint and stroke. - * - * @param aShape the desired shape - */ - public PSWTPath(final Shape aShape) { - this(); - setShape(aShape); - } - - // **************************************************************** - // Stroke - // **************************************************************** - /** - * Returns the paint to use when drawing the stroke of the shape. - * - * @return path's stroke paint - */ - public Paint getStrokePaint() { - return strokePaint; - } - - /** - * Sets the paint to use when drawing the stroke of the shape. - * - * @param strokeColor new stroke color - */ - public void setStrokeColor(final Paint strokeColor) { - final Paint old = strokePaint; - strokePaint = strokeColor; - invalidatePaint(); - firePropertyChange(PPath.PROPERTY_CODE_STROKE_PAINT, PPath.PROPERTY_STROKE_PAINT, old, strokePaint); - } - - /** - * Set the bounds of this path. This method works by scaling the path to fit - * into the specified bounds. This normally works well, but if the specified - * base bounds get too small then it is impossible to expand the path shape - * again since all its numbers have tended to zero, so application code may - * need to take this into consideration. - * - * @param x new left position of bounds - * @param y new top position of bounds - * @param width the new width of the bounds - * @param height the new height of the bounds - */ - protected void internalUpdateBounds(final double x, final double y, final double width, final double height) { - if (updatingBoundsFromPath) { - return; - } - if (origShape == null) { - return; - } - - final Rectangle2D pathBounds = origShape.getBounds2D(); - - if (Math.abs(x - pathBounds.getX()) / x < BOUNDS_TOLERANCE - && Math.abs(y - pathBounds.getY()) / y < BOUNDS_TOLERANCE - && Math.abs(width - pathBounds.getWidth()) / width < BOUNDS_TOLERANCE - && Math.abs(height - pathBounds.getHeight()) / height < BOUNDS_TOLERANCE) { - return; - } - - if (internalXForm == null) { - internalXForm = new PAffineTransform(); - } - internalXForm.setToIdentity(); - internalXForm.translate(x, y); - internalXForm.scale(width / pathBounds.getWidth(), height / pathBounds.getHeight()); - internalXForm.translate(-pathBounds.getX(), -pathBounds.getY()); - - try { - inverseXForm = internalXForm.createInverse(); - } - catch (final Exception e) { - throw new PAffineTransformException("unable to invert transform", internalXForm); - } - } - - /** - * Returns true if path crosses the provided bounds. Takes visibility of - * path into account. - * - * @param aBounds bounds being tested for intersection - * @return true if path visibly crosses bounds - */ - public boolean intersects(final Rectangle2D aBounds) { - if (super.intersects(aBounds)) { - final Rectangle2D srcBounds; - if (internalXForm == null) { - srcBounds = aBounds; - } - else { - srcBounds = new PBounds(aBounds); - internalXForm.inverseTransform(srcBounds, srcBounds); - } - - if (getPaint() != null && shape.intersects(srcBounds)) { - return true; - } - else if (strokePaint != null) { - return BASIC_STROKE.createStrokedShape(shape).intersects(srcBounds); - } - } - return false; - } - - /** - * Recalculates the path's bounds by examining it's associated shape. - */ - public void updateBoundsFromPath() { - updatingBoundsFromPath = true; - - if (origShape == null) { - resetBounds(); - } - else { - final Rectangle2D b = origShape.getBounds2D(); - - // Note that this pen width code does not really work for SWT since - // it assumes - // that the pen width scales - in actuality it does not. However, - // the fix would - // be to have volatile bounds for all shapes which isn't a nice - // alternative - super.setBounds(b.getX() - PEN_WIDTH, b.getY() - PEN_WIDTH, b.getWidth() + 2 * PEN_WIDTH, b.getHeight() + 2 - * PEN_WIDTH); - } - updatingBoundsFromPath = false; - } - - // **************************************************************** - // Painting - // **************************************************************** - /** - * Paints the path on the context provided. - * - * @param paintContext the context onto which the path will be painted - */ - protected void paint(final PPaintContext paintContext) { - final Paint p = getPaint(); - final SWTGraphics2D g2 = (SWTGraphics2D) paintContext.getGraphics(); - - if (internalXForm != null) { - g2.transform(internalXForm); - } - - if (p != null) { - g2.setBackground((Color) p); - fillShape(g2); - } - - if (strokePaint != null) { - g2.setColor((Color) strokePaint); - drawShape(g2); - } - - if (inverseXForm != null) { - g2.transform(inverseXForm); - } - } - - private void drawShape(final SWTGraphics2D g2) { - final double lineWidth = g2.getTransformedLineWidth(); - if (shape instanceof Rectangle2D) { - g2.drawRect(shapePts[0] + lineWidth / 2, shapePts[1] + lineWidth / 2, shapePts[2] - lineWidth, shapePts[3] - - lineWidth); - } - else if (shape instanceof Ellipse2D) { - g2.drawOval(shapePts[0] + lineWidth / 2, shapePts[1] + lineWidth / 2, shapePts[2] - lineWidth, shapePts[3] - - lineWidth); - } - else if (shape instanceof Arc2D) { - g2.drawArc(shapePts[0] + lineWidth / 2, shapePts[1] + lineWidth / 2, shapePts[2] - lineWidth, shapePts[3] - - lineWidth, shapePts[4], shapePts[5]); - } - else if (shape instanceof RoundRectangle2D) { - g2.drawRoundRect(shapePts[0] + lineWidth / 2, shapePts[1] + lineWidth / 2, shapePts[2] - lineWidth, - shapePts[3] - lineWidth, shapePts[4], shapePts[5]); - } - else { - g2.draw(shape); - } - } - - private void fillShape(final SWTGraphics2D g2) { - final double lineWidth = g2.getTransformedLineWidth(); - if (shape instanceof Rectangle2D) { - g2.fillRect(shapePts[0] + lineWidth / 2, shapePts[1] + lineWidth / 2, shapePts[2] - lineWidth, shapePts[3] - - lineWidth); - } - else if (shape instanceof Ellipse2D) { - g2.fillOval(shapePts[0] + lineWidth / 2, shapePts[1] + lineWidth / 2, shapePts[2] - lineWidth, shapePts[3] - - lineWidth); - } - else if (shape instanceof Arc2D) { - g2.fillArc(shapePts[0] + lineWidth / 2, shapePts[1] + lineWidth / 2, shapePts[2] - lineWidth, shapePts[3] - - lineWidth, shapePts[4], shapePts[5]); - } - else if (shape instanceof RoundRectangle2D) { - g2.fillRoundRect(shapePts[0] + lineWidth / 2, shapePts[1] + lineWidth / 2, shapePts[2] - lineWidth, - shapePts[3] - lineWidth, shapePts[4], shapePts[5]); - } - else { - g2.fill(shape); - } - } - - /** - * Changes the underlying shape of this PSWTPath. - * - * @param newShape new associated shape of this PSWTPath - */ - public void setShape(final Shape newShape) { - shape = cloneShape(newShape); - origShape = shape; - updateShapePoints(newShape); - - firePropertyChange(PPath.PROPERTY_CODE_PATH, PPath.PROPERTY_PATH, null, shape); - updateBoundsFromPath(); - invalidatePaint(); - } - - /** - * Updates the internal points used to draw the shape. - * - * @param aShape shape to read points from - */ - public void updateShapePoints(final Shape aShape) { - if (aShape instanceof Rectangle2D) { - if (shapePts == null || shapePts.length < 4) { - shapePts = new double[4]; - } - - shapePts[0] = ((Rectangle2D) shape).getX(); - shapePts[1] = ((Rectangle2D) shape).getY(); - shapePts[2] = ((Rectangle2D) shape).getWidth(); - shapePts[3] = ((Rectangle2D) shape).getHeight(); - } - else if (aShape instanceof Ellipse2D) { - if (shapePts == null || shapePts.length < 4) { - shapePts = new double[4]; - } - - shapePts[0] = ((Ellipse2D) shape).getX(); - shapePts[1] = ((Ellipse2D) shape).getY(); - shapePts[2] = ((Ellipse2D) shape).getWidth(); - shapePts[3] = ((Ellipse2D) shape).getHeight(); - } - else if (aShape instanceof Arc2D) { - if (shapePts == null || shapePts.length < 6) { - shapePts = new double[6]; - } - - shapePts[0] = ((Arc2D) shape).getX(); - shapePts[1] = ((Arc2D) shape).getY(); - shapePts[2] = ((Arc2D) shape).getWidth(); - shapePts[3] = ((Arc2D) shape).getHeight(); - shapePts[4] = ((Arc2D) shape).getAngleStart(); - shapePts[5] = ((Arc2D) shape).getAngleExtent(); - } - else if (aShape instanceof RoundRectangle2D) { - if (shapePts == null || shapePts.length < 6) { - shapePts = new double[6]; - } - - shapePts[0] = ((RoundRectangle2D) shape).getX(); - shapePts[1] = ((RoundRectangle2D) shape).getY(); - shapePts[2] = ((RoundRectangle2D) shape).getWidth(); - shapePts[3] = ((RoundRectangle2D) shape).getHeight(); - shapePts[4] = ((RoundRectangle2D) shape).getArcWidth(); - shapePts[5] = ((RoundRectangle2D) shape).getArcHeight(); - } - else { - shapePts = SWTShapeManager.shapeToPolyline(shape); - } - } - - /** - * Clone's the shape provided. - * - * @param aShape shape to be cloned - * - * @return a cloned version of the provided shape - */ - public Shape cloneShape(final Shape aShape) { - if (aShape instanceof Rectangle2D) { - return new PBounds((Rectangle2D) aShape); - } - else if (aShape instanceof Ellipse2D) { - final Ellipse2D e2 = (Ellipse2D) aShape; - return new Ellipse2D.Double(e2.getX(), e2.getY(), e2.getWidth(), e2.getHeight()); - } - else if (aShape instanceof Arc2D) { - final Arc2D a2 = (Arc2D) aShape; - return new Arc2D.Double(a2.getX(), a2.getY(), a2.getWidth(), a2.getHeight(), a2.getAngleStart(), a2 - .getAngleExtent(), a2.getArcType()); - } - else if (aShape instanceof RoundRectangle2D) { - final RoundRectangle2D r2 = (RoundRectangle2D) aShape; - return new RoundRectangle2D.Double(r2.getX(), r2.getY(), r2.getWidth(), r2.getHeight(), r2.getArcWidth(), - r2.getArcHeight()); - } - else if (aShape instanceof Line2D) { - final Line2D l2 = (Line2D) aShape; - return new Line2D.Double(l2.getP1(), l2.getP2()); - } - else { - final GeneralPath aPath = new GeneralPath(); - aPath.append(aShape, false); - return aPath; - } - } - - /** - * Resets the path to a rectangle with the dimensions and position provided. - * - * @param x left of the rectangle - * @param y top of te rectangle - * @param width width of the rectangle - * @param height height of the rectangle - */ - public void setPathToRectangle(final float x, final float y, final float width, final float height) { - TEMP_RECTANGLE.setFrame(x, y, width, height); - setShape(TEMP_RECTANGLE); - } - - /** - * Resets the path to a rectangle with the dimensions and position provided. - * - * @param x left of the rectangle - * @param y top of te rectangle - * @param width width of the rectangle - * @param height height of the rectangle - * @param arcWidth width of arc in the corners of the rectangle - * @param arcHeight height of arc in the corners of the rectangle - */ - public void setPathToRoundRectangle(final float x, final float y, final float width, final float height, - final float arcWidth, final float arcHeight) { - TEMP_ROUNDRECTANGLE.setRoundRect(x, y, width, height, arcWidth, arcHeight); - setShape(TEMP_ROUNDRECTANGLE); - } - - /** - * Resets the path to an ellipse positioned at the coordinate provided with - * the dimensions provided. - * - * @param x left of the ellipse - * @param y top of the ellipse - * @param width width of the ellipse - * @param height height of the ellipse - */ - public void setPathToEllipse(final float x, final float y, final float width, final float height) { - TEMP_ELLIPSE.setFrame(x, y, width, height); - setShape(TEMP_ELLIPSE); - } - - /** - * Sets the path to a sequence of segments described by the points. - * - * @param points points to that lie along the generated path - */ - public void setPathToPolyline(final Point2D[] points) { - final GeneralPath path = new GeneralPath(); - path.reset(); - path.moveTo((float) points[0].getX(), (float) points[0].getY()); - for (int i = 1; i < points.length; i++) { - path.lineTo((float) points[i].getX(), (float) points[i].getY()); - } - setShape(path); - } - - /** - * Sets the path to a sequence of segments described by the point components - * provided. - * - * @param xp the x components of the points along the path - * @param yp the y components of the points along the path - */ - public void setPathToPolyline(final float[] xp, final float[] yp) { - final GeneralPath path = new GeneralPath(); - path.reset(); - path.moveTo(xp[0], yp[0]); - for (int i = 1; i < xp.length; i++) { - path.lineTo(xp[i], yp[i]); - } - setShape(path); - } - - /** - * Return the center of this SWT path node, based on its bounds. - * - * @return the center of this SWT path node, based on its bounds - */ - public Point2D getCenter() { - PBounds bounds = getBoundsReference(); - return new Point2D.Double(bounds.x + (bounds.width / 2.0), bounds.y + (bounds.height / 2.0)); - } -} \ No newline at end of file diff --git a/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTRoot.java b/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTRoot.java deleted file mode 100644 index 74e0630..0000000 --- a/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTRoot.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.swt; - -import java.awt.event.ActionListener; - -import javax.swing.Timer; - -import org.eclipse.swt.widgets.Composite; -import org.piccolo2d.PRoot; - - -/** - * PSWTRoot is a subclass of PRoot that is designed to work in the SWT - * environment. In particular it uses SWTTimers and the SWT event dispatch - * thread. With the current setup only a single PSWTCanvas is expected to be - * connected to a root. - * - * @version 1.1 - * @author Jesse Grosjean - */ -public class PSWTRoot extends PRoot { - private static final long serialVersionUID = 1L; - private final Composite composite; - - /** - * Constructs a PSWTRoot attached to the provided composite. - * - * @param composite composite PSWTRoot is responsible for - */ - public PSWTRoot(final Composite composite) { - this.composite = composite; - } - - /** - * Creates a timer that will fire the listener every delay milliseconds. - * - * @param delay time in milliseconds between firings of listener - * @param listener listener to be fired - * - * @return the created timer - */ - public Timer createTimer(final int delay, final ActionListener listener) { - return new SWTTimer(composite.getDisplay(), delay, listener); - } - - /** - * Processes Inputs if any kind of IO needs to be done. - */ - public void scheduleProcessInputsIfNeeded() { - if (!Thread.currentThread().equals(composite.getDisplay().getThread())) { - return; - } - - if (!processInputsScheduled && !processingInputs - && (getFullBoundsInvalid() || getChildBoundsInvalid() || getPaintInvalid() || getChildPaintInvalid())) { - - processInputsScheduled = true; - composite.getDisplay().asyncExec(new Runnable() { - public void run() { - processInputs(); - processInputsScheduled = false; - } - }); - } - } -} diff --git a/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTSelectionEventHandler.java b/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTSelectionEventHandler.java deleted file mode 100644 index c9fabd9..0000000 --- a/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTSelectionEventHandler.java +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.swt; - -import java.awt.Color; -import java.awt.geom.Point2D; -import java.awt.geom.Rectangle2D; -import java.util.List; - -import org.eclipse.swt.SWT; -import org.piccolo2d.PCamera; -import org.piccolo2d.PNode; -import org.piccolo2d.event.PInputEvent; -import org.piccolo2d.extras.event.PSelectionEventHandler; -import org.piccolo2d.util.PBounds; -import org.piccolo2d.util.PPaintContext; - - -/** - * Selection event handler modified to use SWT paths instead of normal paths. - * - * @version 1.0 - * @author Lance Good - */ -public class PSWTSelectionEventHandler extends PSelectionEventHandler { - - PSWTPath marquee; - PNode marqueeParent; - Point2D pressPt; - Point2D canvasPressPt; - - /** - * Creates a selection event handler. - * - * @param marqueeParent The node to which the event handler dynamically adds - * a marquee (temporarily) to represent the area being selected. - * @param selectableParent The node whose children will be selected by this - * event handler. - */ - public PSWTSelectionEventHandler(final PNode marqueeParent, final PNode selectableParent) { - super(new PNode(), selectableParent); - this.marqueeParent = marqueeParent; - } - - /** - * Creates a selection event handler. - * - * @param marqueeParent The node to which the event handler dynamically adds - * a marquee (temporarily) to represent the area being selected. - * @param selectableParents A list of nodes whose children will be selected - * by this event handler. - */ - public PSWTSelectionEventHandler(final PNode marqueeParent, final List selectableParents) { - super(new PNode(), selectableParents); - this.marqueeParent = marqueeParent; - } - - /** - * Modifies the provided node so that it is displayed as selected. - * - * @param node node to be decorated - */ - public void decorateSelectedNode(final PNode node) { - PSWTBoundsHandle.addBoundsHandlesTo(node); - } - - /** - * Undoes any modifications to the provided node so that it is not displayed as selected. - * - * @param node node to be undecorated - */ - public void undecorateSelectedNode(final PNode node) { - PSWTBoundsHandle.removeBoundsHandlesFrom(node); - } - - /** {@inheritDoc} */ - protected void initializeSelection(final PInputEvent pie) { - super.initializeSelection(pie); - pressPt = pie.getPosition(); - canvasPressPt = pie.getCanvasPosition(); - } - - /** {@inheritDoc} */ - protected void initializeMarquee(final PInputEvent e) { - super.initializeMarquee(e); - - marquee = new PSWTPath(new Rectangle2D.Float((float) pressPt.getX(), (float) pressPt.getY(), 0, 0)) { - /** - * - */ - private static final long serialVersionUID = 1L; - - protected void paint(final PPaintContext paintContext) { - final SWTGraphics2D s2g = (SWTGraphics2D) paintContext.getGraphics(); - s2g.gc.setLineStyle(SWT.LINE_DASH); - super.paint(paintContext); - s2g.gc.setLineStyle(SWT.LINE_SOLID); - } - }; - marquee.setStrokeColor(Color.black); - marquee.setPaint(null); - marqueeParent.addChild(marquee); - } - - /** {@inheritDoc} */ - protected void updateMarquee(final PInputEvent pie) { - super.updateMarquee(pie); - - final PBounds b = new PBounds(); - - if (marqueeParent instanceof PCamera) { - b.add(canvasPressPt); - b.add(pie.getCanvasPosition()); - } - else { - b.add(pressPt); - b.add(pie.getPosition()); - } - - marquee.setPathToRectangle((float) b.x, (float) b.y, (float) b.width, (float) b.height); - b.reset(); - b.add(pressPt); - b.add(pie.getPosition()); - } - - /** {@inheritDoc} */ - protected PBounds getMarqueeBounds() { - if (marquee != null) { - return marquee.getBounds(); - } - return new PBounds(); - } - - /** {@inheritDoc} */ - protected void endMarqueeSelection(final PInputEvent e) { - super.endMarqueeSelection(e); - - marquee.removeFromParent(); - marquee = null; - } - - /** {@inheritDoc} */ - protected void dragActivityStep(final PInputEvent aEvent) { - } -} \ No newline at end of file diff --git a/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTStickyHandleManager.java b/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTStickyHandleManager.java deleted file mode 100644 index f8b8861..0000000 --- a/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTStickyHandleManager.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.swt; - -import org.piccolo2d.PCamera; -import org.piccolo2d.PNode; -import org.piccolo2d.util.PBounds; -import org.piccolo2d.util.PPickPath; - -/** - * A class for managing the position of a sticky handle. - */ -public class PSWTStickyHandleManager extends PNode { - private static final long serialVersionUID = 1L; - private PNode target; - private PCamera camera; - - /** - * Creates a sticky handle that will be displayed on the given camera and - * will update the provided target. - * - * @param camera camera on which to display the sticky handle - * @param target target being controlled by the handle - */ - public PSWTStickyHandleManager(final PCamera camera, final PNode target) { - setCameraTarget(camera, target); - PSWTBoundsHandle.addBoundsHandlesTo(this); - } - - /** - * Changes the associated camera and target for this sticky handle. - * - * @param newCamera new camera onto which this handle should appear - * @param newTarget new target which this handle will control - */ - public void setCameraTarget(final PCamera newCamera, final PNode newTarget) { - camera = newCamera; - camera.addChild(this); - target = newTarget; - } - - /** {@inheritDoc} */ - public boolean setBounds(final double x, final double y, final double width, final double height) { - final PBounds b = new PBounds(x, y, width, height); - camera.localToGlobal(b); - camera.localToView(b); - target.globalToLocal(b); - target.setBounds(b); - return super.setBounds(x, y, width, height); - } - - /** - * Always returns true to ensure that they will always be displayed - * appropriately. - * - * @return true - */ - protected boolean getBoundsVolatile() { - return true; - } - - /** {@inheritDoc} */ - public PBounds getBoundsReference() { - final PBounds targetBounds = target.getFullBounds(); - camera.viewToLocal(targetBounds); - camera.globalToLocal(targetBounds); - final PBounds bounds = super.getBoundsReference(); - bounds.setRect(targetBounds); - return super.getBoundsReference(); - } - - /** {@inheritDoc} */ - public void startResizeBounds() { - super.startResizeBounds(); - target.startResizeBounds(); - } - - /** {@inheritDoc} */ - public void endResizeBounds() { - super.endResizeBounds(); - target.endResizeBounds(); - } - - /** - * Since PSWTStickyHandle manager is not visible on screen, it just returns - * false when it is asked to be repainted. - * - * @param pickPath path of this node in which the interaction occurred that - * required the repaint - * - * @return always false - */ - public boolean pickAfterChildren(final PPickPath pickPath) { - return false; - } -} diff --git a/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTText.java b/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTText.java deleted file mode 100644 index 3c2bc21..0000000 --- a/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTText.java +++ /dev/null @@ -1,528 +0,0 @@ -/** - * Copyright (C) 1998-1999 by University of Maryland, College Park, MD 20742, USA - * All rights reserved. - */ -package edu.umd.cs.piccolox.swt; - -import java.awt.Color; -import java.awt.Font; -import java.awt.Graphics2D; -import java.awt.Paint; -import java.awt.geom.AffineTransform; -import java.awt.geom.Point2D; -import java.util.ArrayList; -import java.util.Iterator; - -import org.eclipse.swt.graphics.FontMetrics; -import org.eclipse.swt.graphics.GC; -import org.eclipse.swt.graphics.Point; -import org.eclipse.swt.widgets.Display; -import org.piccolo2d.PNode; -import org.piccolo2d.util.PPaintContext; - - -/** - * PSWTText creates a visual component to support text. Multiple lines - * can be entered, and basic editing is supported. A caret is drawn, and can be - * repositioned with mouse clicks. The text object is positioned so that its - * upper-left corner is at the origin, though this can be changed with the - * translate methods. - */ -public class PSWTText extends PNode { - private static final long serialVersionUID = 1L; - - /** Below this magnification render text as 'greek'. */ - protected static final double DEFAULT_GREEK_THRESHOLD = 5.5; - - /** Default color of text rendered as 'greek'. */ - protected static final Color DEFAULT_GREEK_COLOR = Color.gray; - - /** Default font name of text. */ - protected static final String DEFAULT_FONT_NAME = "Helvetica"; - - /** Default font style for text. */ - protected static final int DEFAULT_FONT_STYLE = Font.PLAIN; - - /** Default font size for text. */ - protected static final int DEFAULT_FONT_SIZE = 12; - - /** Default font for text. */ - protected static final Font DEFAULT_FONT = new Font(DEFAULT_FONT_NAME, DEFAULT_FONT_STYLE, DEFAULT_FONT_SIZE); - - /** Default color for text. */ - protected static final Color DEFAULT_PEN_COLOR = Color.black; - - /** Default text when new text area is created. */ - protected static final String DEFAULT_TEXT = ""; - - /** Default background transparency state. */ - protected static final boolean DEFAULT_IS_TRANSPARENT = false; - - /** Default padding. */ - protected static final int DEFAULT_PADDING = 2; - - /** Whether the text be drawn with a transparent background. */ - private boolean transparent = DEFAULT_IS_TRANSPARENT; - - /** Below this magnification text is rendered as greek. */ - protected double greekThreshold = DEFAULT_GREEK_THRESHOLD; - - /** Color for greek text. */ - protected Color greekColor = DEFAULT_GREEK_COLOR; - - /** Current pen color. */ - protected Color penColor = DEFAULT_PEN_COLOR; - - /** Current text font. */ - protected Font font = DEFAULT_FONT; - - /** The amount of padding on each side of the text. */ - protected int padding = DEFAULT_PADDING; - - /** Each element is one line of text. */ - protected ArrayList lines = new ArrayList(); - - /** Translation offset X. */ - protected double translateX = 0.0; - - /** Translation offset Y. */ - protected double translateY = 0.0; - - /** Default constructor for PSWTTest. */ - public PSWTText() { - this(DEFAULT_TEXT, DEFAULT_FONT); - } - - /** - * PSWTTest constructor with initial text. - * - * @param str The initial text. - */ - public PSWTText(final String str) { - this(str, DEFAULT_FONT); - } - - /** - * PSWTTest constructor with initial text and font. - * - * @param str The initial text. - * @param font The font for this PSWTText component. - */ - public PSWTText(final String str, final Font font) { - setText(str); - this.font = font; - - recomputeBounds(); - } - - /** - * Returns the current pen color. - * - * @return current pen color - */ - public Color getPenColor() { - return penColor; - } - - /** - * Sets the current pen color. - * - * @param color use this color. - */ - public void setPenColor(final Color color) { - penColor = color; - repaint(); - } - - /** - * Returns the current pen paint. - * - * @return the current pen paint - */ - public Paint getPenPaint() { - return penColor; - } - - /** - * Sets the current pen paint. - * - * @param aPaint use this paint. - */ - public void setPenPaint(final Paint aPaint) { - penColor = (Color) aPaint; - } - - /** - * Returns the current background color. - * - * @return the current background color - */ - public Color getBackgroundColor() { - return (Color) getPaint(); - } - - /** - * Sets the current background color. - * - * @param color use this color. - */ - public void setBackgroundColor(final Color color) { - super.setPaint(color); - } - - /** - * Sets whether the text should be drawn in transparent mode, i.e., whether - * the background should be drawn or not. - * - * @param transparent the new transparency of the background - */ - public void setTransparent(final boolean transparent) { - this.transparent = transparent; - } - - /** - * Returns whether the text should be drawn using the transparent mode, - * i.e., whether the background should be drawn or not. - * - * @return true if background will not be drawn - */ - public boolean isTransparent() { - return transparent; - } - - /** - * Returns the current greek threshold. Below this magnification text is - * rendered as 'greek'. - * - * @return magnification at which the text will not be drawn and a blank - * rectangle will appear instead - */ - public double getGreekThreshold() { - return greekThreshold; - } - - /** - * Sets the current greek threshold. Below this magnification text is - * rendered as 'greek'. - * - * @param threshold compared to renderContext magnification. - */ - public void setGreekThreshold(final double threshold) { - greekThreshold = threshold; - repaint(); - } - - /** - * Returns the current font. - * - * @return current font in node - */ - public Font getFont() { - return font; - } - - /** - * Return the text within this text component. Multiline text is returned as - * a single string where each line is separated by a newline character. - * Single line text does not have any newline characters. - * - * @return string containing this node's text - */ - public String getText() { - StringBuffer result = new StringBuffer(); - - final Iterator lineIterator = lines.iterator(); - while (lineIterator.hasNext()) { - result.append(lineIterator.next()); - result.append('\n'); - } - - if (result.length() > 0) { - result.deleteCharAt(result.length() - 1); - } - - return result.toString(); - } - - /** - * Sets the font for the text. - *
- * Warning: Java has a serious bug in that it does not support very - * small fonts. In particular, fonts that are less than about a pixel high - * just don't work. Since in Jazz, it is common to create objects of - * arbitrary sizes, and then scale them, an application can easily create a - * text object with a very small font by accident. The workaround for this - * bug is to create a larger font for the text object, and then scale the - * node down correspondingly. - * - * @param aFont use this font. - */ - public void setFont(final Font aFont) { - font = aFont; - - recomputeBounds(); - } - - /** - * Sets the text of this visual component to str. Multiple lines of text are - * separated by a newline character. - * - * @param str use this string. - */ - public void setText(final String str) { - int pos = 0; - int index; - boolean done = false; - lines.clear(); - do { - index = str.indexOf('\n', pos); - if (index == -1) { - lines.add(str.substring(pos)); - done = true; - } - else { - lines.add(str.substring(pos, index)); - pos = index + 1; - } - } while (!done); - - recomputeBounds(); - } - - /** - * Set text translation offset X. - * - * @param x the X translation. - */ - public void setTranslateX(final double x) { - setTranslation(x, translateY); - } - - /** - * Get the X offset translation. - * - * @return the X translation. - */ - public double getTranslateX() { - return translateX; - } - - /** - * Set text translation offset Y. - * - * @param y the Y translation. - */ - public void setTranslateY(final double y) { - setTranslation(translateX, y); - } - - /** - * Get the Y offset translation. - * - * @return the Y translation. - */ - public double getTranslateY() { - return translateY; - } - - /** - * Set the text translation offset to the specified position. - * - * @param x the X component of translation - * @param y the Y component of translation - */ - public void setTranslation(final double x, final double y) { - translateX = x; - translateY = y; - - recomputeBounds(); - } - - /** - * Set the text translation offset to point p. - * - * @param p The translation offset. - */ - public void setTranslation(final Point2D p) { - setTranslation(p.getX(), p.getY()); - } - - /** - * Get the text translation offset. - * - * @return The translation offset. - */ - public Point2D getTranslation() { - final Point2D p = new Point2D.Double(translateX, translateY); - return p; - } - - /** - * Renders the text object. - *
- * The transform, clip, and composite will be set appropriately when this
- * object is rendered. It is up to this object to restore the transform,
- * clip, and composite of the Graphics2D if this node changes any of them.
- * However, the color, font, and stroke are unspecified by Jazz. This object
- * should set those things if they are used, but they do not need to be
- * restored.
- *
- * @param ppc Contains information about current render.
- */
- public void paint(final PPaintContext ppc) {
- if (lines.isEmpty()) {
- return;
- }
-
- final Graphics2D g2 = ppc.getGraphics();
- AffineTransform at = null;
- boolean translated = false;
-
- if (translateX != 0.0 || translateY != 0.0) {
- at = g2.getTransform();
- g2.translate(translateX, translateY);
- translated = true;
- }
-
- final double renderedFontSize = font.getSize() * ppc.getScale();
-
- // If font is too small then render it as "greek"
- if (renderedFontSize < greekThreshold) {
- paintAsGreek(ppc);
- }
- else {
- paintAsText(ppc);
- }
-
- if (translated) {
- g2.setTransform(at);
- }
- }
-
- /**
- * Paints this object as greek.
- *
- * @param ppc The graphics context to paint into.
- */
- public void paintAsGreek(final PPaintContext ppc) {
- final Graphics2D g2 = ppc.getGraphics();
-
- if (greekColor != null) {
- g2.setBackground(greekColor);
- ((SWTGraphics2D) g2).fillRect(0, 0, getWidth(), getHeight());
- }
- }
-
- /**
- * Paints this object normally (show it's text). Note that the entire text
- * gets rendered so that it's upper left corner appears at the origin of
- * this local object.
- *
- * @param ppc The graphics context to paint into.
- */
- public void paintAsText(final PPaintContext ppc) {
- final SWTGraphics2D sg2 = (SWTGraphics2D) ppc.getGraphics();
-
- if (!transparent) {
- if (getPaint() == null) {
- sg2.setBackground(Color.WHITE);
- }
- else {
- sg2.setBackground((Color) getPaint());
- }
-
- sg2.fillRect(0, 0, (int) getWidth(), (int) getHeight());
- }
-
- sg2.translate(padding, padding);
-
- sg2.setColor(penColor);
- sg2.setFont(font);
-
- String line;
- double y = 0;
-
- final FontMetrics fontMetrics = sg2.getSWTFontMetrics();
-
- final Iterator lineIterator = lines.iterator();
- while (lineIterator.hasNext()) {
- line = (String) lineIterator.next();
- if (line.length() != 0) {
- sg2.drawString(line, 0, y, true);
- }
-
- y += fontMetrics.getHeight();
- }
-
- sg2.translate(-padding, -padding);
- }
-
- /**
- * Recalculates this node's bounding box by examining it's text content.
- */
- protected void recomputeBounds() {
- final GC gc = new GC(Display.getDefault());
-
- final Point newBounds;
- if (isTextEmpty()) {
- // If no text, then we want to have the bounds of a space character,
- // so get those bounds here
- newBounds = gc.stringExtent(" ");
- }
- else {
- newBounds = calculateTextBounds(gc);
- }
-
- gc.dispose();
-
- setBounds(translateX, translateY, newBounds.x + 2 * DEFAULT_PADDING, newBounds.y + 2 * DEFAULT_PADDING);
- }
-
- /**
- * Determines if this node's text is essentially empty.
- *
- * @return true if the text is the empty string
- */
- private boolean isTextEmpty() {
- return lines.isEmpty() || lines.size() == 1 && ((String) lines.get(0)).equals("");
- }
-
- /**
- * Calculates the bounds of the text in the box as measured by the given
- * graphics context and font metrics.
- *
- * @param gc graphics context from which the measurements are done
- * @return point representing the dimensions of the text's bounds
- */
- private Point calculateTextBounds(final GC gc) {
- final SWTGraphics2D g2 = new SWTGraphics2D(gc, Display.getDefault());
- g2.setFont(font);
- final FontMetrics fm = g2.getSWTFontMetrics();
- final Point textBounds = new Point(0, 0);
-
- boolean firstLine = true;
-
- final Iterator lineIterator = lines.iterator();
- while (lineIterator.hasNext()) {
- String line = (String) lineIterator.next();
- Point lineBounds = gc.stringExtent(line);
- if (firstLine) {
- textBounds.x = lineBounds.x;
- textBounds.y += fm.getAscent() + fm.getDescent() + fm.getLeading();
- firstLine = false;
- }
- else {
- textBounds.x = Math.max(lineBounds.x, textBounds.x);
- textBounds.y += fm.getHeight();
- }
- }
-
- return textBounds;
- }
-
- /** {@inheritDoc} */
- protected void internalUpdateBounds(final double x, final double y, final double width, final double height) {
- recomputeBounds();
- }
-
-}
diff --git a/swt/src/main/java/edu/umd/cs/piccolox/swt/SWTGraphics2D.java b/swt/src/main/java/edu/umd/cs/piccolox/swt/SWTGraphics2D.java
deleted file mode 100644
index 0e34a9e..0000000
--- a/swt/src/main/java/edu/umd/cs/piccolox/swt/SWTGraphics2D.java
+++ /dev/null
@@ -1,1532 +0,0 @@
-/*
- * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org
- * Copyright (c) 1998-2008, University of Maryland
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification, are permitted provided
- * that the following conditions are met:
- *
- * Redistributions of source code must retain the above copyright notice, this list of conditions
- * and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
- * and the following disclaimer in the documentation and/or other materials provided with the
- * distribution.
- *
- * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its
- * contributors may be used to endorse or promote products derived from this software without specific
- * prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
- * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
- * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package edu.umd.cs.piccolox.swt;
-
-import java.awt.Color;
-import java.awt.Composite;
-import java.awt.Font;
-import java.awt.FontMetrics;
-import java.awt.Graphics;
-import java.awt.Graphics2D;
-import java.awt.GraphicsConfiguration;
-import java.awt.Image;
-import java.awt.Paint;
-import java.awt.Point;
-import java.awt.Rectangle;
-import java.awt.RenderingHints;
-import java.awt.Shape;
-import java.awt.Stroke;
-import java.awt.RenderingHints.Key;
-import java.awt.font.FontRenderContext;
-import java.awt.font.GlyphVector;
-import java.awt.geom.AffineTransform;
-import java.awt.geom.Arc2D;
-import java.awt.geom.Ellipse2D;
-import java.awt.geom.NoninvertibleTransformException;
-import java.awt.geom.PathIterator;
-import java.awt.geom.Rectangle2D;
-import java.awt.geom.RoundRectangle2D;
-import java.awt.image.BufferedImage;
-import java.awt.image.BufferedImageOp;
-import java.awt.image.ImageObserver;
-import java.awt.image.RenderedImage;
-import java.awt.image.renderable.RenderableImage;
-import java.text.AttributedCharacterIterator;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.graphics.Device;
-import org.eclipse.swt.graphics.FontData;
-import org.eclipse.swt.graphics.GC;
-import org.eclipse.swt.graphics.Path;
-import org.eclipse.swt.graphics.Transform;
-
-/**
- * An extension to Graphics2D to support an SWT Piccolo Canvas with little
- * modification to the current Piccolo architecture
- *
- * There is an outstanding SWT bug request #33319 for more efficient
- * polyline/polygon rendering methods. It also appears that most of the code
- * below could be made obselete by bug fix #6490
- *
- * A lot of this may also be duplicated in GEF - the eclipse Graphical Editor
- * Framework
- *
- * @author Lance Good
- */
-public class SWTGraphics2D extends Graphics2D {
- private static final int DEFAULT_FONT_SIZE = 12;
-
- private static final boolean DEFAULT_STRING_TRANSPARENCY = true;
-
- /**
- * The number of Graphics Contexts active as determined by called to
- * incrementGCCount and decrementGCCount.
- */
- protected static int CACHE_COUNT = 0;
- /** Map from font names to Fonts. */
- protected static final HashMap FONT_CACHE = new HashMap();
- /** Map from awt colors to swt colors. */
- protected static final HashMap COLOR_CACHE = new HashMap();
- /** Map from awt shapess to swt Paths. */
- protected static final HashMap SHAPE_CACHE = new HashMap();
- /** Buffer used to extract the graphics device. */
- protected static final BufferedImage BUFFER = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
-
- private static final Point TEMP_POINT = new Point();
- private static final Rectangle2D TEMP_RECT = new Rectangle2D.Double();
- private static final Rectangle2D TEMP_LINE_RECT = new Rectangle2D.Double();
- private static final org.eclipse.swt.graphics.Rectangle SWT_RECT = new org.eclipse.swt.graphics.Rectangle(0, 0, 0, 0);
-
- /** The Underlying GraphicsContext provided by swt. */
- protected GC gc;
- /** Device onto which all graphics operations will ultimately take place. */
- protected Device device;
- /** The current transform to apply to drawing operations. */
- protected AffineTransform transform = new AffineTransform();
- private final Transform swtTransform;
- /** The current font to use when drawing text. */
- protected org.eclipse.swt.graphics.Font curFont;
- /** The current stroke width to use when drawing lines. */
- protected double lineWidth = 1.0;
-
- /**
- * Constructor for SWTGraphics2D.
- *
- * @param gc The Eclipse Graphics Context onto which all Graphics2D
- * operations are delegating
- * @param device Device onto which ultimately all gc operations are drawn
- * onto
- */
- public SWTGraphics2D(final GC gc, final Device device) {
- this.gc = gc;
- this.device = device;
-
- swtTransform = new Transform(device);
- gc.setAntialias(SWT.ON);
- }
-
- // //////////////////
- // GET CLIP
- // //////////////////
-
- /** {@inheritDoc} */
- public Rectangle getClipBounds() {
- final org.eclipse.swt.graphics.Rectangle rect = gc.getClipping();
- final Rectangle aRect = new Rectangle(rect.x, rect.y, rect.width, rect.height);
- try {
- SWTShapeManager.transform(aRect, transform.createInverse());
- }
- catch (final Exception e) {
- throw new RuntimeException(e);
- }
- return aRect;
- }
-
- /** {@inheritDoc} */
- public void clipRect(final int x, final int y, final int width, final int height) {
- TEMP_RECT.setRect(x, y, width, height);
- SWTShapeManager.transform(TEMP_RECT, transform);
- SWTShapeManager.awtToSWT(TEMP_RECT, SWT_RECT);
-
- org.eclipse.swt.graphics.Rectangle clip = gc.getClipping();
- clip = clip.intersection(SWT_RECT);
-
- gc.setClipping(clip);
- }
-
- /** {@inheritDoc} */
- public void setClip(final int x, final int y, final int width, final int height) {
- TEMP_RECT.setRect(x, y, width, height);
- SWTShapeManager.transform(TEMP_RECT, transform);
- SWTShapeManager.awtToSWT(TEMP_RECT, SWT_RECT);
-
- gc.setClipping(SWT_RECT);
- }
-
- /**
- * This method isn't really supported by SWT - so will use the shape bounds.
- *
- * @param s shape of the clipping region to apply to graphics operations
- */
- public void clip(final Shape s) {
- final Rectangle2D clipBds = s.getBounds2D();
- SWTShapeManager.transform(clipBds, transform);
- SWTShapeManager.awtToSWT(clipBds, SWT_RECT);
-
- org.eclipse.swt.graphics.Rectangle clip = gc.getClipping();
- clip = clip.intersection(SWT_RECT);
-
- gc.setClipping(SWT_RECT);
- }
-
- /**
- * This method isn't really supported by SWT - so will use the shape bounds.
- *
- * @param clip the desired clipping region's shape, will be simplified to
- * its bounds
- */
- public void setClip(final Shape clip) {
- if (clip == null) {
- gc.setClipping((org.eclipse.swt.graphics.Rectangle) null);
- }
- else {
- final Rectangle2D clipBds = clip.getBounds2D();
- SWTShapeManager.transform(clipBds, transform);
- SWTShapeManager.awtToSWT(clipBds, SWT_RECT);
-
- gc.setClipping(SWT_RECT);
- }
- }
-
- /** {@inheritDoc} */
- public Shape getClip() {
- final org.eclipse.swt.graphics.Rectangle rect = gc.getClipping();
- final Rectangle2D aRect = new Rectangle2D.Double(rect.x, rect.y, rect.width, rect.height);
- try {
- SWTShapeManager.transform(aRect, transform.createInverse());
- }
- catch (final NoninvertibleTransformException e) {
- throw new RuntimeException(e);
- }
- return aRect;
- }
-
- /**
- * Returns a dummy device configuration.
- *
- * @return a dummy device configuration
- */
- public GraphicsConfiguration getDeviceConfiguration() {
- return ((Graphics2D) BUFFER.getGraphics()).getDeviceConfiguration();
- }
-
- // //////////////
- // COLOR METHODS
- // //////////////
-
- /** {@inheritDoc} */
- public Paint getPaint() {
- return getColor();
- }
-
- /** {@inheritDoc} */
- public void setPaint(final Paint paint) {
- if (paint instanceof Color) {
- setColor((Color) paint);
- }
- }
-
- /** {@inheritDoc} */
- public Color getColor() {
- final org.eclipse.swt.graphics.Color color = gc.getForeground();
- final Color awtColor = new Color(color.getRed(), color.getGreen(), color.getBlue());
- return awtColor;
- }
-
- /** {@inheritDoc} */
- public void setColor(final Color c) {
- org.eclipse.swt.graphics.Color cachedColor = (org.eclipse.swt.graphics.Color) COLOR_CACHE.get(c);
- if (cachedColor == null) {
- cachedColor = new org.eclipse.swt.graphics.Color(device, c.getRed(), c.getGreen(), c.getBlue());
- COLOR_CACHE.put(c, cachedColor);
- }
- gc.setForeground(cachedColor);
- }
-
- /**
- * Sets the foreground color to the provided swt color.
- *
- * @param foregroundColor new foreground color
- */
- public void setColor(final org.eclipse.swt.graphics.Color foregroundColor) {
- gc.setForeground(foregroundColor);
- }
-
- /** {@inheritDoc} */
- public void setBackground(final Color c) {
- org.eclipse.swt.graphics.Color cachedColor = (org.eclipse.swt.graphics.Color) COLOR_CACHE.get(c);
- if (cachedColor == null) {
- cachedColor = new org.eclipse.swt.graphics.Color(device, c.getRed(), c.getGreen(), c.getBlue());
- COLOR_CACHE.put(c, cachedColor);
- }
- gc.setBackground(cachedColor);
- }
-
- /**
- * Sets the background color to the provided swt color.
- *
- * @param backgroundColor new background color
- */
- public void setBackground(final org.eclipse.swt.graphics.Color backgroundColor) {
- gc.setBackground(backgroundColor);
- }
-
- /** {@inheritDoc} */
- public Color getBackground() {
- final org.eclipse.swt.graphics.Color color = gc.getBackground();
- final Color awtColor = new Color(color.getRed(), color.getGreen(), color.getBlue());
- return awtColor;
- }
-
- // //////////////
- // FONT METHODS
- // //////////////
-
- /**
- * Returns the current swt font to use when drawing.
- *
- * @return current swt font
- */
- public org.eclipse.swt.graphics.Font getSWTFont() {
- return curFont;
- }
-
- /**
- * Returns the font metrics of the current SWT font.
- *
- * @return font metrics of the current SWT font
- */
- public org.eclipse.swt.graphics.FontMetrics getSWTFontMetrics() {
- gc.setFont(curFont);
- return gc.getFontMetrics();
- }
-
- /** {@inheritDoc} */
- public Font getFont() {
- if (curFont != null) {
- int style = Font.PLAIN;
-
- final FontData[] fd = curFont.getFontData();
- if (fd.length > 0) {
- if ((fd[0].getStyle() & SWT.BOLD) != 0) {
- style = style | Font.BOLD;
- }
- if ((fd[0].getStyle() & SWT.ITALIC) != 0) {
- style = style | SWT.ITALIC;
- }
-
- return new Font(fd[0].getName(), style, fd[0].getHeight());
- }
- return null;
- }
- else {
- return null;
- }
- }
-
- /** {@inheritDoc} */
- public void setFont(final Font font) {
- // TODO: prevent NPE
- final String fontString = "name=" + font.getFamily() + ";bold=" + font.isBold() + ";italic=" + font.isItalic()
- + ";size=" + font.getSize();
-
- curFont = getFont(fontString);
- }
-
- /**
- * Set the font for this SWTGraphics2D to font
.
- *
- * @param font font for this SWTGraphics2D
- */
- public void setFont(final org.eclipse.swt.graphics.Font font) {
- curFont = font;
- }
-
- /**
- * Returns the SWT font matching the given font string.
- *
- * @param fontString description of the font desired
- * @return matching font, or null if not found
- */
- public org.eclipse.swt.graphics.Font getFont(final String fontString) {
- org.eclipse.swt.graphics.Font cachedFont = (org.eclipse.swt.graphics.Font) FONT_CACHE.get(fontString);
- if (cachedFont == null) {
- int style = 0;
- if (fontString.indexOf("bold=true") != -1) {
- style = style | SWT.BOLD;
- }
- if (fontString.indexOf("italic=true") != -1) {
- style = style | SWT.ITALIC;
- }
-
- final String name = fontString.substring(0, fontString.indexOf(";"));
- final String size = fontString.substring(fontString.lastIndexOf(";") + 1, fontString.length());
- int sizeInt = DEFAULT_FONT_SIZE;
- try {
- sizeInt = Integer.parseInt(size.substring(size.indexOf("=") + 1, size.length()));
- }
- catch (final Exception e) {
- throw new RuntimeException(e);
- }
-
- cachedFont = new org.eclipse.swt.graphics.Font(device,
- name.substring(name.indexOf("=") + 1, name.length()), sizeInt, style);
- FONT_CACHE.put(fontString, cachedFont);
- }
- return cachedFont;
- }
-
- // /////////////////////////
- // AFFINE TRANSFORM METHODS
- // /////////////////////////
-
- /** {@inheritDoc} */
- public void translate(final int x, final int y) {
- transform.translate(x, y);
- updateSWTTransform();
- }
-
- /** {@inheritDoc} */
- public void translate(final double tx, final double ty) {
- transform.translate(tx, ty);
- updateSWTTransform();
- }
-
- /** {@inheritDoc} */
- public void rotate(final double theta) {
- transform.rotate(theta);
- updateSWTTransform();
- }
-
- /** {@inheritDoc} */
- public void rotate(final double theta, final double x, final double y) {
- transform.rotate(theta, x, y);
- updateSWTTransform();
- }
-
- /** {@inheritDoc} */
- public void scale(final double sx, final double sy) {
- transform.scale(sx, sy);
- updateSWTTransform();
- }
-
- /** {@inheritDoc} */
- public void shear(final double shx, final double shy) {
- transform.shear(shx, shy);
- updateSWTTransform();
- }
-
- /** {@inheritDoc} */
- public void transform(final AffineTransform srcTransform) {
- transform.concatenate(srcTransform);
- updateSWTTransform();
- }
-
- /** {@inheritDoc} */
- public void setTransform(final AffineTransform newTransform) {
- transform = (AffineTransform) newTransform.clone();
- updateSWTTransform();
- }
-
- /** {@inheritDoc} */
- public AffineTransform getTransform() {
- return (AffineTransform) transform.clone();
- }
-
- // SUPPORT METHODS
- // /////////////////////////////
-
- /**
- * Updates the SWT transform instance such that it matches AWTs counterpart.
- */
- private void updateSWTTransform() {
- final double[] m = new double[6];
- transform.getMatrix(m);
- swtTransform.setElements((float) m[0], (float) m[1], (float) m[2], (float) m[3], (float) m[4], (float) m[5]);
- }
-
- /**
- * Converts a java 2d path iterator to a SWT path.
- *
- * @param iter specifies the iterator to be converted.
- * @return the corresponding path object. Must be disposed() when no longer
- * used.
- */
- private Path pathIterator2Path(final PathIterator iter) {
- final float[] coords = new float[6];
-
- final Path path = new Path(device);
-
- while (!iter.isDone()) {
- final int type = iter.currentSegment(coords);
-
- switch (type) {
- case PathIterator.SEG_MOVETO:
- path.moveTo(coords[0], coords[1]);
- break;
-
- case PathIterator.SEG_LINETO:
- path.lineTo(coords[0], coords[1]);
- break;
-
- case PathIterator.SEG_CLOSE:
- path.close();
- break;
-
- case PathIterator.SEG_QUADTO:
- path.quadTo(coords[0], coords[1], coords[2], coords[3]);
- break;
-
- case PathIterator.SEG_CUBICTO:
- path.cubicTo(coords[0], coords[1], coords[2], coords[3], coords[4], coords[5]);
- break;
- default:
- // log this?
- }
-
- iter.next();
- }
- return path;
- }
-
- /** {@inheritDoc} */
- public void clearRect(final int x, final int y, final int width, final int height) {
- fillRect(x, y, width, height);
- }
-
- /** {@inheritDoc} */
- public void draw(final Shape s) {
- if (s instanceof Rectangle2D) {
- final Rectangle2D r2 = (Rectangle2D) s;
- drawRect(r2.getX(), r2.getY(), r2.getWidth(), r2.getHeight());
- }
- else if (s instanceof Ellipse2D) {
- final Ellipse2D e2 = (Ellipse2D) s;
- drawOval(e2.getX(), e2.getY(), e2.getWidth(), e2.getHeight());
- }
- else if (s instanceof RoundRectangle2D) {
- final RoundRectangle2D r2 = (RoundRectangle2D) s;
- drawRoundRect(r2.getX(), r2.getY(), r2.getWidth(), r2.getHeight(), r2.getArcWidth(), r2.getArcHeight());
- }
- else if (s instanceof Arc2D) {
- final Arc2D a2 = (Arc2D) s;
- drawArc(a2.getX(), a2.getY(), a2.getWidth(), a2.getHeight(), a2.getAngleStart(), a2.getAngleExtent());
- }
- else {
- Path p = (Path) SHAPE_CACHE.get(s);
- if (p == null) {
- p = pathIterator2Path(s.getPathIterator(null));
- SHAPE_CACHE.put(s, p);
- }
- drawPath(p);
- }
- }
-
- /** {@inheritDoc} */
- public void fill(final Shape s) {
- if (s instanceof Rectangle2D) {
- final Rectangle2D r2 = (Rectangle2D) s;
- fillRect(r2.getX(), r2.getY(), r2.getWidth(), r2.getHeight());
- }
- else if (s instanceof Ellipse2D) {
- final Ellipse2D e2 = (Ellipse2D) s;
- fillOval(e2.getX(), e2.getY(), e2.getWidth(), e2.getHeight());
- }
- else if (s instanceof RoundRectangle2D) {
- final RoundRectangle2D r2 = (RoundRectangle2D) s;
- fillRoundRect(r2.getX(), r2.getY(), r2.getWidth(), r2.getHeight(), r2.getArcWidth(), r2.getArcHeight());
- }
- else if (s instanceof Arc2D) {
- final Arc2D a2 = (Arc2D) s;
- fillArc(a2.getX(), a2.getY(), a2.getWidth(), a2.getHeight(), a2.getAngleStart(), a2.getAngleExtent());
- }
- else {
- Path p = (Path) SHAPE_CACHE.get(s);
- if (p == null) {
- p = pathIterator2Path(s.getPathIterator(null));
- SHAPE_CACHE.put(s, p);
- }
- drawPath(p);
- }
- }
-
- /** {@inheritDoc} */
- public void drawPolyline(final int[] xPoints, final int[] yPoints, final int nPoints) {
- final int[] ptArray = new int[2 * nPoints];
- for (int i = 0; i < nPoints; i++) {
- TEMP_POINT.setLocation(xPoints[i], yPoints[i]);
- transform.transform(TEMP_POINT, TEMP_POINT);
- ptArray[2 * i] = xPoints[i];
- ptArray[2 * i + 1] = yPoints[i];
- }
-
- gc.setLineWidth(getTransformedLineWidth());
- gc.drawPolyline(ptArray);
- }
-
- /**
- * Draw a polyline from the specified double array of points.
- *
- * @param pts double array of points
- */
- public void drawPolyline(final double[] pts) {
- final int[] intPts = SWTShapeManager.transform(pts, transform);
- gc.drawPolyline(intPts);
- }
-
- /** {@inheritDoc} */
- public void drawPolygon(final int[] xPoints, final int[] yPoints, final int nPoints) {
- final int[] ptArray = new int[2 * nPoints];
- for (int i = 0; i < nPoints; i++) {
- TEMP_POINT.setLocation(xPoints[i], yPoints[i]);
- transform.transform(TEMP_POINT, TEMP_POINT);
- ptArray[2 * i] = xPoints[i];
- ptArray[2 * i + 1] = yPoints[i];
- }
-
- gc.drawPolygon(ptArray);
- }
-
- /**
- * Fill a polyline from the specified double array of points.
- *
- * @param pts double array of points
- */
- public void fillPolygon(final double[] pts) {
- final int[] intPts = SWTShapeManager.transform(pts, transform);
- gc.fillPolygon(intPts);
- }
-
- /** {@inheritDoc} */
- public void fillPolygon(final int[] xPoints, final int[] yPoints, final int nPoints) {
- final int[] ptArray = new int[2 * nPoints];
- for (int i = 0; i < nPoints; i++) {
- TEMP_POINT.setLocation(xPoints[i], yPoints[i]);
- transform.transform(TEMP_POINT, TEMP_POINT);
- ptArray[2 * i] = xPoints[i];
- ptArray[2 * i + 1] = yPoints[i];
- }
-
- gc.fillPolygon(ptArray);
- }
-
- /** {@inheritDoc} */
- public void drawLine(final int x1, final int y1, final int x2, final int y2) {
- drawLine((double) x1, (double) y1, (double) x2, (double) y2);
- }
-
- /**
- * Draws a line, using the current color, between the points (x1, y1) and
- * (x2, y2) in this graphics context's coordinate system.
- *
- * @param x1 the first point's x coordinate.
- * @param y1 the first point's y coordinate.
- * @param x2 the second point's x coordinate.
- * @param y2 the second point's y coordinate.
- */
- public void drawLine(final double x1, final double y1, final double x2, final double y2) {
- TEMP_POINT.setLocation(x1, y1);
- transform.transform(TEMP_POINT, TEMP_POINT);
- final double transformedX1 = (int) TEMP_POINT.getX();
- final double transformedY1 = (int) TEMP_POINT.getY();
- TEMP_POINT.setLocation(x2, y2);
- transform.transform(TEMP_POINT, TEMP_POINT);
- final double transformedX2 = (int) TEMP_POINT.getX();
- final double transformedY2 = (int) TEMP_POINT.getY();
-
- gc.setLineWidth(getTransformedLineWidth());
- gc.drawLine((int) (transformedX1 + 0.5), (int) (transformedY1 + 0.5), (int) (transformedX2 + 0.5),
- (int) (transformedY2 + 0.5));
- }
-
- // **************************************************************************
- // *
- // FOR NOW - ASSUME NO ROTATION ON THE TRANSFORM FOR THE FOLLOWING CALLS!
- // **************************************************************************
- // *
-
- /**
- * Copies the image to the specified position.
- *
- * @param img swt image to be copied
- * @param x x component of position
- * @param y y component of position
- */
- public void copyArea(final org.eclipse.swt.graphics.Image img, final double x, final double y) {
- TEMP_POINT.setLocation(x, y);
- transform.transform(TEMP_POINT, TEMP_POINT);
-
- gc.copyArea(img, (int) (TEMP_POINT.getX() + 0.5), (int) (TEMP_POINT.getY() + 0.5));
- }
-
- /** {@inheritDoc} */
- public void copyArea(final int x, final int y, final int width, final int height, final int dx, final int dy) {
- TEMP_RECT.setRect(x, y, width, height);
- SWTShapeManager.transform(TEMP_RECT, transform);
-
- TEMP_POINT.setLocation(dx, dy);
- transform.transform(TEMP_POINT, TEMP_POINT);
- gc.copyArea((int) TEMP_RECT.getX(), (int) TEMP_RECT.getY(), (int) TEMP_RECT.getWidth(), (int) TEMP_RECT
- .getHeight(), (int) TEMP_POINT.getX(), (int) TEMP_POINT.getY());
- }
-
- /**
- * Renders the text of the specified String, using the current text
- * attribute state in the Graphics2D context. The baseline of the first
- * character is at position (x, y) in the User Space. The rendering
- * attributes applied include the Clip, Transform, Paint, Font and Composite
- * attributes. For characters in script systems such as Hebrew and Arabic,
- * the glyphs can be rendered from right to left, in which case the
- * coordinate supplied is the location of the leftmost character on the
- * baseline.
- *
- * @param str the string to be rendered
- * @param x the x coordinate of the location where the String should be
- * rendered
- * @param y the y coordinate of the location where the String should be
- * rendered
- * @param isTransparent whether a background should be painted behind the
- * text
- */
- public void drawString(final String str, final int x, final int y, final boolean isTransparent) {
- gc.setTransform(swtTransform);
- gc.drawString(str, x, y, isTransparent);
- gc.setTransform(null);
- }
-
- /** {@inheritDoc} */
- public void drawString(final String str, final int x, final int y) {
- drawString(str, x, y, DEFAULT_STRING_TRANSPARENCY);
- }
-
- /**
- * Renders the text of the specified String, using the current text
- * attribute state in the Graphics2D context. The baseline of the first
- * character is at position (x, y) in the User Space. The rendering
- * attributes applied include the Clip, Transform, Paint, Font and Composite
- * attributes. For characters in script systems such as Hebrew and Arabic,
- * the glyphs can be rendered from right to left, in which case the
- * coordinate supplied is the location of the leftmost character on the
- * baseline.
- *
- * @param str the string to be rendered
- * @param x the x coordinate of the location where the String should be
- * rendered
- * @param y the y coordinate of the location where the String should be
- * rendered
- */
- public void drawString(final String str, final double x, final double y) {
- drawString(str, (int) (x + 0.5), (int) (y + 0.5));
- }
-
- /**
- * Renders the text of the specified String, using the current text
- * attribute state in the Graphics2D context. The baseline of the first
- * character is at position (x, y) in the User Space. The rendering
- * attributes applied include the Clip, Transform, Paint, Font and Composite
- * attributes. For characters in script systems such as Hebrew and Arabic,
- * the glyphs can be rendered from right to left, in which case the
- * coordinate supplied is the location of the leftmost character on the
- * baseline.
- *
- * @param str the string to be rendered
- * @param x the x coordinate of the location where the String should be
- * rendered
- * @param y the y coordinate of the location where the String should be
- * rendered
- * @param isTransparent whether a background should be painted behind the
- * text
- */
- public void drawString(final String str, final double x, final double y, final boolean isTransparent) {
- drawString(str, (int) (x + 0.5), (int) (y + 0.5), isTransparent);
- }
-
- /** {@inheritDoc} */
- public void drawString(final String str, final float x, final float y) {
- drawString(str, (int) (x + 0.5), (int) (y + 0.5));
- }
-
- /**
- * Renders the text of the specified String, using the current text
- * attribute state in the Graphics2D context. The baseline of the first
- * character is at position (x, y) in the User Space. The rendering
- * attributes applied include the Clip, Transform, Paint, Font and Composite
- * attributes. For characters in script systems such as Hebrew and Arabic,
- * the glyphs can be rendered from right to left, in which case the
- * coordinate supplied is the location of the leftmost character on the
- * baseline.
- *
- * @param str the string to be rendered
- * @param x the x coordinate of the location where the String should be
- * rendered
- * @param y the y coordinate of the location where the String should be
- * rendered
- */
- public void drawText(final String str, final double x, final double y) {
- drawString(str, (int) (x + 0.5), (int) (y + 0.5));
- }
-
- /**
- * Renders the text of the specified String, using the current text
- * attribute state in the Graphics2D context. The baseline of the first
- * character is at position (x, y) in the User Space. The rendering
- * attributes applied include the Clip, Transform, Paint, Font and Composite
- * attributes. For characters in script systems such as Hebrew and Arabic,
- * the glyphs can be rendered from right to left, in which case the
- * coordinate supplied is the location of the leftmost character on the
- * baseline.
- *
- * @param str the string to be rendered
- * @param x the x coordinate of the location where the String should be
- * rendered
- * @param y the y coordinate of the location where the String should be
- * rendered
- * @param flags flags to apply to the string as defined by SWT
- */
- public void drawText(final String str, final double x, final double y, final int flags) {
- drawText(str, (int) (x + 0.5), (int) (y + 0.5), flags);
- }
-
- /**
- * Renders the text of the specified String, using the current text
- * attribute state in the Graphics2D context. The baseline of the first
- * character is at position (x, y) in the User Space. The rendering
- * attributes applied include the Clip, Transform, Paint, Font and Composite
- * attributes. For characters in script systems such as Hebrew and Arabic,
- * the glyphs can be rendered from right to left, in which case the
- * coordinate supplied is the location of the leftmost character on the
- * baseline.
- *
- * @param str the string to be rendered
- * @param x the x coordinate of the location where the String should be
- * rendered
- * @param y the y coordinate of the location where the String should be
- * rendered
- * @param flags flags to apply to the string as defined by SWT
- */
- public void drawText(final String str, final int x, final int y, final int flags) {
- gc.setTransform(swtTransform);
- gc.drawText(str, x, y, flags);
- gc.setTransform(null);
- }
-
- /** {@inheritDoc} */
- public void drawRect(final int x, final int y, final int width, final int height) {
- drawRect((double) x, (double) y, (double) width, (double) height);
- }
-
- /**
- * Draws the outline of the specified rectangle. The left and right edges of
- * the rectangle are at x and x + width. The top and bottom edges are at y
- * and y + height. The rectangle is drawn using the graphics context's
- * current color.
- *
- * @param x the x coordinate of the rectangle to be drawn.
- * @param y the y coordinate of the rectangle to be drawn.
- * @param width the width of the rectangle to be drawn.
- * @param height the height of the rectangle to be drawn.
- */
- public void drawRect(final double x, final double y, final double width, final double height) {
- TEMP_RECT.setRect(x, y, width, height);
- SWTShapeManager.transform(TEMP_RECT, transform);
- SWTShapeManager.awtToSWT(TEMP_RECT, SWT_RECT);
-
- gc.setLineWidth(getTransformedLineWidth());
- gc.drawRectangle(SWT_RECT);
- }
-
- /** {@inheritDoc} */
- public void fillRect(final int x, final int y, final int width, final int height) {
- fillRect((double) x, (double) y, (double) width, (double) height);
- }
-
- /**
- * Fills the specified rectangle. The left and right edges of the rectangle
- * are at x and x + width - 1. The top and bottom edges are at y and y +
- * height - 1. The resulting rectangle covers an area width pixels wide by
- * height pixels tall. The rectangle is filled using the graphics context's
- * current color.
- *
- * @param x the x coordinate of the rectangle to be filled.
- * @param y the y coordinate of the rectangle to be filled.
- * @param width the width of the rectangle to be filled.
- * @param height the height of the rectangle to be filled.
- */
- public void fillRect(final double x, final double y, final double width, final double height) {
- TEMP_RECT.setRect(x, y, width, height);
- SWTShapeManager.transform(TEMP_RECT, transform);
- SWTShapeManager.awtToSWT(TEMP_RECT, SWT_RECT);
-
- gc.fillRectangle(SWT_RECT);
- }
-
- /** {@inheritDoc} */
- public void drawRoundRect(final int x, final int y, final int width, final int height, final int arcWidth,
- final int arcHeight) {
- drawRoundRect((double) x, (double) y, (double) width, (double) height, (double) arcWidth, (double) arcHeight);
- }
-
- /**
- * Draws an outlined round-cornered rectangle using this graphics context's
- * current color. The left and right edges of the rectangle are at x and x +
- * width, respectively. The top and bottom edges of the rectangle are at y
- * and y + height.
- *
- * @param x the x coordinate of the rectangle to be drawn.
- * @param y the y coordinate of the rectangle to be drawn.
- * @param width the width of the rectangle to be drawn.
- * @param height the height of the rectangle to be drawn.
- * @param arcWidth the horizontal diameter of the arc at the four corners.
- * @param arcHeight the vertical diameter of the arc at the four corners.
- */
- public void drawRoundRect(final double x, final double y, final double width, final double height,
- final double arcWidth, final double arcHeight) {
- TEMP_RECT.setRect(x, y, width, height);
- SWTShapeManager.transform(TEMP_RECT, transform);
- final double tx = TEMP_RECT.getX();
- final double ty = TEMP_RECT.getY();
- final double twidth = TEMP_RECT.getWidth();
- final double theight = TEMP_RECT.getHeight();
-
- TEMP_RECT.setRect(0, 0, arcWidth, arcHeight);
- SWTShapeManager.transform(TEMP_RECT, transform);
- final double tarcWidth = TEMP_RECT.getWidth();
- final double tarcHeight = TEMP_RECT.getHeight();
-
- gc.setLineWidth(getTransformedLineWidth());
- gc.drawRoundRectangle((int) (tx + 0.5), (int) (ty + 0.5), (int) (twidth + 0.5), (int) (theight + 0.5),
- (int) (tarcWidth + 0.5), (int) (tarcHeight + 0.5));
- }
-
- /** {@inheritDoc} */
- public void fillRoundRect(final int x, final int y, final int width, final int height, final int arcWidth,
- final int arcHeight) {
- fillRoundRect((double) x, (double) y, (double) width, (double) height, (double) arcWidth, (double) arcHeight);
- }
-
- /**
- * Fills the specified rounded corner rectangle with the current color. The
- * left and right edges of the rectangle are at x and x + width - 1,
- * respectively. The top and bottom edges of the rectangle are at y and y +
- * height - 1.
- *
- *@param x the x coordinate of the rectangle to be filled.
- *@param y the y coordinate of the rectangle to be filled.
- *@param width the width of the rectangle to be filled.
- *@param height the height of the rectangle to be filled.
- *@param arcWidth the horizontal diameter of the arc at the four corners.
- *@param arcHeight the vertical diameter of the arc at the four corners.
- */
- public void fillRoundRect(final double x, final double y, final double width, final double height,
- final double arcWidth, final double arcHeight) {
- TEMP_RECT.setRect(x, y, width, height);
- SWTShapeManager.transform(TEMP_RECT, transform);
- final double tx = TEMP_RECT.getX();
- final double ty = TEMP_RECT.getY();
- final double twidth = TEMP_RECT.getWidth();
- final double theight = TEMP_RECT.getHeight();
-
- TEMP_RECT.setRect(0, 0, arcWidth, arcHeight);
- SWTShapeManager.transform(TEMP_RECT, transform);
- final double tarcWidth = TEMP_RECT.getWidth();
- final double tarcHeight = TEMP_RECT.getHeight();
-
- gc.setLineWidth(getTransformedLineWidth());
- gc.fillRoundRectangle((int) (tx + 0.5), (int) (ty + 0.5), (int) (twidth + 0.5), (int) (theight + 0.5),
- (int) (tarcWidth + 0.5), (int) (tarcHeight + 0.5));
- }
-
- /** {@inheritDoc} */
- public void drawOval(final int x, final int y, final int width, final int height) {
- drawOval((double) x, (double) y, (double) width, (double) height);
- }
-
- /**
- * Draws the outline of an oval. The result is a circle or ellipse that fits
- * within the rectangle specified by the x, y, width, and height arguments.
- * The oval covers an area that is width + 1 pixels wide and height + 1
- * pixels tall.
- *
- * @param x the x coordinate of the upper left corner of the oval to be
- * drawn.
- * @param y the y coordinate of the upper left corner of the oval to be
- * drawn.
- * @param width the width of the oval to be drawn.
- * @param height the height of the oval to be drawn.
- */
- public void drawOval(final double x, final double y, final double width, final double height) {
- TEMP_RECT.setRect(x, y, width, height);
- SWTShapeManager.transform(TEMP_RECT, transform);
-
- gc.setLineWidth(getTransformedLineWidth());
- gc.drawOval((int) (TEMP_RECT.getX() + 0.5), (int) (TEMP_RECT.getY() + 0.5), (int) (TEMP_RECT.getWidth() + 0.5),
- (int) (TEMP_RECT.getHeight() + 0.5));
- }
-
- /** {@inheritDoc} */
- public void fillOval(final int x, final int y, final int width, final int height) {
- fillOval((double) x, (double) y, (double) width, (double) height);
- }
-
- /**
- * Fills an oval bounded by the specified rectangle with the current color.
- *
- * @param x the x coordinate of the upper left corner of the oval to be
- * filled.
- * @param y the y coordinate of the upper left corner of the oval to be
- * filled.
- * @param width the width of the oval to be filled.
- * @param height the height of the oval to be filled.
- */
- public void fillOval(final double x, final double y, final double width, final double height) {
- TEMP_RECT.setRect(x, y, width, height);
- SWTShapeManager.transform(TEMP_RECT, transform);
-
- gc.fillOval((int) (TEMP_RECT.getX() + 0.5), (int) (TEMP_RECT.getY() + 0.5), (int) (TEMP_RECT.getWidth() + 0.5),
- (int) (TEMP_RECT.getHeight() + 0.5));
- }
-
- /** {@inheritDoc} */
- public void drawArc(final int x, final int y, final int width, final int height, final int startAngle,
- final int extent) {
- drawArc((double) x, (double) y, (double) width, (double) height, (double) startAngle, (double) extent);
- }
-
- /**
- * Draws the outline of a circular or elliptical arc covering the specified
- * rectangle.
- *
- * The resulting arc begins at startAngle and extends for arcAngle degrees,
- * using the current color. Angles are interpreted such that 0 degrees is at
- * the 3 o'clock position. A positive value indicates a counter-clockwise
- * rotation while a negative value indicates a clockwise rotation.
- *
- * The center of the arc is the center of the rectangle whose origin is (x,
- * y) and whose size is specified by the width and height arguments.
- *
- * The resulting arc covers an area width + 1 pixels wide by height + 1
- * pixels tall.
- *
- * The angles are specified relative to the non-square extents of the
- * bounding rectangle such that 45 degrees always falls on the line from the
- * center of the ellipse to the upper right corner of the bounding
- * rectangle. As a result, if the bounding rectangle is noticeably longer in
- * one axis than the other, the angles to the start and end of the arc
- * segment will be skewed farther along the longer axis of the bounds.
- *
- * @param x the x coordinate of the upper-left corner of the arc to be
- * drawn.
- * @param y the y coordinate of the upper-left corner of the arc to be
- * drawn.
- * @param width the width of the arc to be drawn.
- * @param height the height of the arc to be drawn.
- * @param startAngle the beginning angle.
- * @param extent the angular extent of the arc, relative to the start angle.
- */
- public void drawArc(final double x, final double y, final double width, final double height,
- final double startAngle, final double extent) {
- TEMP_RECT.setRect(x, y, width, height);
- SWTShapeManager.transform(TEMP_RECT, transform);
-
- gc.setLineWidth(getTransformedLineWidth());
- gc.drawArc((int) (TEMP_RECT.getX() + 0.5), (int) (TEMP_RECT.getY() + 0.5), (int) (TEMP_RECT.getWidth() + 0.5),
- (int) (TEMP_RECT.getHeight() + 0.5), (int) (startAngle + 0.5), (int) (startAngle + extent + 0.5));
- }
-
- /** {@inheritDoc} */
- public void fillArc(final int x, final int y, final int width, final int height, final int startAngle,
- final int extent) {
- drawArc((double) x, (double) y, (double) width, (double) height, (double) startAngle, (double) extent);
- }
-
- /**
- * Draws a filledArc with the options provided.
- *
- * @param x the x coordinate of the upper-left corner of the arc to be
- * filled.
- * @param y the y coordinate of the upper-left corner of the arc to be
- * filled.
- * @param width the width of the arc to be filled.
- * @param height the height of the arc to be filled.
- * @param startAngle the beginning angle.
- * @param extent the angular extent of the arc, relative to the start angle.
- */
- public void fillArc(final double x, final double y, final double width, final double height,
- final double startAngle, final double extent) {
- TEMP_RECT.setRect(x, y, width, height);
- SWTShapeManager.transform(TEMP_RECT, transform);
-
- gc.drawArc((int) (TEMP_RECT.getX() + 0.5), (int) (TEMP_RECT.getY() + 0.5), (int) (TEMP_RECT.getWidth() + 0.5),
- (int) (TEMP_RECT.getHeight() + 0.5), (int) (startAngle + 0.5), (int) (startAngle + extent + 0.5));
- }
-
- /**
- * Draws the provided path.
- *
- * @param p path to draw
- */
- public void drawPath(final Path p) {
- gc.setTransform(swtTransform);
- gc.drawPath(p);
- gc.setTransform(null);
- }
-
- /**
- * Draws a filled version of the provided path.
- *
- * @param p path to draw filled
- */
- public void fillPath(final Path p) {
- gc.setTransform(swtTransform);
- gc.fillPath(p);
- gc.setTransform(null);
- }
-
- /**
- * Draws the provided image at the position specified.
- *
- * @param image image to draw
- * @param x x component of the position
- * @param y y component of the position
- */
- public void drawImage(final org.eclipse.swt.graphics.Image image, final double x, final double y) {
- final org.eclipse.swt.graphics.Rectangle bounds = image.getBounds();
- TEMP_RECT.setRect(x, y, bounds.width, bounds.height);
- SWTShapeManager.transform(TEMP_RECT, transform);
- SWTShapeManager.awtToSWT(TEMP_RECT, SWT_RECT);
-
- gc.drawImage(image, 0, 0, bounds.width, bounds.height, SWT_RECT.x, SWT_RECT.y, SWT_RECT.width, SWT_RECT.height);
- }
-
- /**
- * Draws the source region from the image onto the destination region of the
- * graphics context. Stretching if necessary.
- *
- * @param image image from which to copy
- * @param srcX the left of the source region
- * @param srcY the top of the source region
- * @param srcW the width of the source region
- * @param srcH the height of the source region
- * @param destX the left of the destination region
- * @param destY the top of the destination region
- * @param destW the width of the destination region
- * @param destH the height of the destination region
- */
- public void drawImage(final org.eclipse.swt.graphics.Image image, final int srcX, final int srcY, final int srcW,
- final int srcH, final double destX, final double destY, final double destW, final double destH) {
- TEMP_RECT.setRect(destX, destY, destW, destH);
- SWTShapeManager.transform(TEMP_RECT, transform);
- SWTShapeManager.awtToSWT(TEMP_RECT, SWT_RECT);
-
- gc.drawImage(image, srcX, srcY, srcW, srcH, SWT_RECT.x, SWT_RECT.y, SWT_RECT.width, SWT_RECT.height);
- }
-
- /**
- * Sets the line width to use when drawing shapes.
- *
- * @param lineWidth width of line when drawing shapes
- */
- public void setLineWidth(final double lineWidth) {
- this.lineWidth = lineWidth;
- }
-
- /**
- * Computes the width of the line after it passes through the current
- * transform.
- *
- * @return resulting width of line after being transform
- */
- protected int getTransformedLineWidth() {
- TEMP_LINE_RECT.setRect(0, 0, lineWidth, lineWidth);
- SWTShapeManager.transform(TEMP_LINE_RECT, transform);
-
- return (int) (Math.max(TEMP_LINE_RECT.getWidth(), 1) + 0.5);
- }
-
- /**
- * Fills a gradient rectangle of in the direction specified.
- *
- * @param x left of resulting rectangle
- * @param y top of resulting rectangle
- * @param width width of resulting rectangle
- * @param height height of resulting rectangle
- * @param vertical whether the gradient should be drawn vertically or
- * horizontally
- */
- public void fillGradientRectangle(final double x, final double y, final double width, final double height,
- final boolean vertical) {
- TEMP_RECT.setRect(x, y, width, height);
- SWTShapeManager.transform(TEMP_RECT, transform);
- SWTShapeManager.awtToSWT(TEMP_RECT, SWT_RECT);
-
- gc.fillGradientRectangle(SWT_RECT.x, SWT_RECT.y, SWT_RECT.width, SWT_RECT.height, vertical);
- }
-
- /**
- * Returns the advance width of the character provided in the current font.
- *
- * @param ch character to calculate the advance width of.
- *
- * @return advance width of the character in the current font
- */
- public int getAdvanceWidth(final char ch) {
- final org.eclipse.swt.graphics.Font scaledFont = gc.getFont();
- gc.setFont(curFont);
- final int width = gc.getAdvanceWidth(ch);
- gc.setFont(scaledFont);
- return width;
- }
-
- /**
- * Returns the width of the character provided in the current font.
- *
- * @param ch character to calculate the width of.
- *
- * @return width of the character in the current font
- */
- public int getCharWidth(final char ch) {
- final org.eclipse.swt.graphics.Font scaledFont = gc.getFont();
- gc.setFont(curFont);
- final int width = gc.getCharWidth(ch);
- gc.setFont(scaledFont);
- return width;
- }
-
- /**
- * Returns the extent of the provided string in the current font.
- *
- * @param str string to calculate the extent of.
- *
- * @return extent of the string in the current font
- */
- public org.eclipse.swt.graphics.Point stringExtent(final String str) {
- final org.eclipse.swt.graphics.Font scaledFont = gc.getFont();
- gc.setFont(curFont);
- final org.eclipse.swt.graphics.Point extent = gc.stringExtent(str);
- gc.setFont(scaledFont);
- return extent;
- }
-
- /**
- * Returns the extent of the provided text in the current font.
- *
- * @param str string to calculate the extent of.
- *
- * @return extent of the string in the current font
- */
- public org.eclipse.swt.graphics.Point textExtent(final String str) {
- final org.eclipse.swt.graphics.Font scaledFont = gc.getFont();
- gc.setFont(curFont);
- final org.eclipse.swt.graphics.Point extent = gc.textExtent(str);
- gc.setFont(scaledFont);
- return extent;
- }
-
- /**
- * Returns the extent of the provided text in the current font assuming the
- * flags given.
- *
- * @param str string to calculate the extent of
- * @param flags flags to apply to the rendered font before calculation of
- * extent takes place
- * @return extent of the string in the current font assuming flags provided
- */
- public org.eclipse.swt.graphics.Point textExtent(final String str, final int flags) {
- final org.eclipse.swt.graphics.Font scaledFont = gc.getFont();
- gc.setFont(curFont);
- final org.eclipse.swt.graphics.Point extent = gc.textExtent(str, flags);
- gc.setFont(scaledFont);
- return extent;
- }
-
- // ///////////////////////////////
- // CURRENTLY UNSUPPORTED METHODS
- // ///////////////////////////////
-
- /** {@inheritDoc} */
- public void drawString(final AttributedCharacterIterator iterator, final int x, final int y) {
- }
-
- /** {@inheritDoc} */
- public void drawString(final AttributedCharacterIterator iterator, final float x, final float y) {
- }
-
- /** {@inheritDoc} */
- public void drawGlyphVector(final GlyphVector g, final float x, final float y) {
- }
-
- /**
- * Returns whether the given rect and shape touch. If onStroke = true then
- * it'll include the width of the stroke when calculating.
- *
- * @param rect rect to test
- * @param s shape to test
- * @param onStroke whether to consider the width of the stroke
- * @return true if they touch
- */
- public boolean hit(final Rectangle rect, final Shape s, final boolean onStroke) {
- return false;
- }
-
- /** {@inheritDoc} */
- public void setComposite(final Composite comp) {
- }
-
- /** {@inheritDoc} */
- public void setStroke(final Stroke s) {
- }
-
- /** {@inheritDoc} */
- public void setRenderingHint(final Key hintKey, final Object hintValue) {
- }
-
- /** {@inheritDoc} */
- public Object getRenderingHint(final Key hintKey) {
- return null;
- }
-
- /**
- * {@inheritDoc}
- *
- * @see java.awt.Graphics2D#setRenderingHints(Map)
- */
- public void setRenderingHints(final Map hints) {
- }
-
- /**
- * {@inheritDoc}
- *
- * @see java.awt.Graphics2D#addRenderingHints(Map)
- */
- public void addRenderingHints(final Map hints) {
- }
-
- /**
- * {@inheritDoc}
- *
- * @see java.awt.Graphics2D#getRenderingHints()
- */
- public RenderingHints getRenderingHints() {
- return null;
- }
-
- /**
- * {@inheritDoc}
- *
- * @see java.awt.Graphics2D#getComposite()
- */
- public Composite getComposite() {
- return null;
- }
-
- /**
- * {@inheritDoc}
- *
- * @see java.awt.Graphics2D#getStroke()
- */
- public Stroke getStroke() {
- return null;
- }
-
- /**
- * {@inheritDoc}
- *
- * @see java.awt.Graphics2D#getFontRenderContext()
- */
- public FontRenderContext getFontRenderContext() {
- return null;
- }
-
- /**
- * {@inheritDoc}
- *
- * @see java.awt.Graphics#create()
- */
- public Graphics create() {
- return null;
- }
-
- /**
- * {@inheritDoc}
- *
- * @see java.awt.Graphics#setPaintMode()
- */
- public void setPaintMode() {
- }
-
- /**
- * {@inheritDoc}
- *
- * @see java.awt.Graphics#setXORMode(Color)
- */
- public void setXORMode(final Color c1) {
- }
-
- /**
- * {@inheritDoc}
- *
- * @see java.awt.Graphics#getFontMetrics(Font)
- */
- public FontMetrics getFontMetrics(final Font f) {
- return null;
- }
-
- /**
- * {@inheritDoc}
- *
- * @see java.awt.Graphics2D#drawImage(Image, AffineTransform, ImageObserver)
- */
- public boolean drawImage(final Image img, final AffineTransform xform, final ImageObserver obs) {
- return false;
- }
-
- /**
- * {@inheritDoc}
- *
- * @see java.awt.Graphics2D#drawImage(BufferedImage, BufferedImageOp, int,
- * int)
- */
- public void drawImage(final BufferedImage img, final BufferedImageOp op, final int x, final int y) {
- }
-
- /**
- * {@inheritDoc}
- *
- * @see java.awt.Graphics2D#drawRenderedImage(RenderedImage,
- * AffineTransform)
- */
- public void drawRenderedImage(final RenderedImage img, final AffineTransform xform) {
- }
-
- /**
- * {@inheritDoc}
- *
- * @see java.awt.Graphics2D#drawRenderableImage(RenderableImage,
- * AffineTransform)
- */
- public void drawRenderableImage(final RenderableImage img, final AffineTransform xform) {
- }
-
- /**
- * {@inheritDoc}
- *
- * @see java.awt.Graphics#drawImage(Image, int, int, ImageObserver)
- */
- public boolean drawImage(final Image img, final int x, final int y, final ImageObserver observer) {
- return false;
- }
-
- /**
- * {@inheritDoc}
- *
- * @see java.awt.Graphics#drawImage(Image, int, int, int, int,
- * ImageObserver)
- */
- public boolean drawImage(final Image img, final int x, final int y, final int width, final int height,
- final ImageObserver observer) {
- return false;
- }
-
- /**
- * {@inheritDoc}
- *
- * @see java.awt.Graphics#drawImage(Image, int, int, Color, ImageObserver)
- */
- public boolean drawImage(final Image img, final int x, final int y, final Color bgcolor,
- final ImageObserver observer) {
- return false;
- }
-
- /**
- * {@inheritDoc}
- *
- * @see java.awt.Graphics#drawImage(Image, int, int, int, int, Color,
- * ImageObserver)
- */
- public boolean drawImage(final Image img, final int x, final int y, final int width, final int height,
- final Color bgcolor, final ImageObserver observer) {
- return false;
- }
-
- /**
- * {@inheritDoc}
- *
- * @see java.awt.Graphics#drawImage(Image, int, int, int, int, int, int,
- * int, int, ImageObserver)
- */
- public boolean drawImage(final Image img, final int dx1, final int dy1, final int dx2, final int dy2,
- final int sx1, final int sy1, final int sx2, final int sy2, final ImageObserver observer) {
- return false;
- }
-
- /**
- * {@inheritDoc}
- *
- * @see java.awt.Graphics#drawImage(Image, int, int, int, int, int, int,
- * int, int, Color, ImageObserver)
- */
- public boolean drawImage(final Image img, final int dx1, final int dy1, final int dx2, final int dy2,
- final int sx1, final int sy1, final int sx2, final int sy2, final Color bgcolor,
- final ImageObserver observer) {
- return false;
- }
-
- /**
- * DO NOTHING - DISPOSED IN RENDERING CLASS.
- */
- public void dispose() {
- }
-
- // ///////////////////////////////
- // CLEAN-UP METHODS
- // ///////////////////////////////
-
- /**
- * Increases the number of uses of this graphics 2d object.
- */
- public static void incrementGCCount() {
- CACHE_COUNT++;
- }
-
- /**
- * Decreases the number of uses of this graphics 2d object.
- */
- public static void decrementGCCount() {
- CACHE_COUNT--;
-
- if (CACHE_COUNT == 0) {
- for (final Iterator i = FONT_CACHE.values().iterator(); i.hasNext();) {
- final org.eclipse.swt.graphics.Font font = (org.eclipse.swt.graphics.Font) i.next();
- font.dispose();
- }
- for (final Iterator i = COLOR_CACHE.values().iterator(); i.hasNext();) {
- final org.eclipse.swt.graphics.Color color = (org.eclipse.swt.graphics.Color) i.next();
- color.dispose();
- }
- for (final Iterator i = SHAPE_CACHE.values().iterator(); i.hasNext();) {
- final Path path = (Path) i.next();
- path.dispose();
- }
- }
- }
-
-}
diff --git a/swt/src/main/java/edu/umd/cs/piccolox/swt/SWTShapeManager.java b/swt/src/main/java/edu/umd/cs/piccolox/swt/SWTShapeManager.java
deleted file mode 100644
index c986b76..0000000
--- a/swt/src/main/java/edu/umd/cs/piccolox/swt/SWTShapeManager.java
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org
- * Copyright (c) 1998-2008, University of Maryland
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification, are permitted provided
- * that the following conditions are met:
- *
- * Redistributions of source code must retain the above copyright notice, this list of conditions
- * and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
- * and the following disclaimer in the documentation and/or other materials provided with the
- * distribution.
- *
- * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its
- * contributors may be used to endorse or promote products derived from this software without specific
- * prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
- * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
- * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package edu.umd.cs.piccolox.swt;
-
-import java.awt.Shape;
-import java.awt.geom.AffineTransform;
-import java.awt.geom.PathIterator;
-import java.awt.geom.Point2D;
-import java.awt.geom.Rectangle2D;
-import java.util.ArrayList;
-
-import org.eclipse.swt.graphics.Rectangle;
-
-/**
- * SWT shape manager.
- *
- * @author Lance Good
- */
-public class SWTShapeManager {
- private static AffineTransform IDENTITY_XFORM = new AffineTransform();
- private static Point2D aPoint = new Point2D.Double();
- private static ArrayList segList = new ArrayList();
- private static double[] pts = new double[8];
-
- /**
- * Apply the specified transform to the specified rectangle, modifying the
- * rect.
- *
- * @param rect The rectangle to be transformed
- * @param at The transform to use to transform the rectangle
- */
- public static void transform(final Rectangle2D rect, final AffineTransform at) {
- // First, transform all 4 corners of the rectangle
- pts[0] = rect.getX(); // top left corner
- pts[1] = rect.getY();
- pts[2] = rect.getX() + rect.getWidth(); // top right corner
- pts[3] = rect.getY();
- pts[4] = rect.getX() + rect.getWidth(); // bottom right corner
- pts[5] = rect.getY() + rect.getHeight();
- pts[6] = rect.getX(); // bottom left corner
- pts[7] = rect.getY() + rect.getHeight();
- at.transform(pts, 0, pts, 0, 4);
-
- // Then, find the bounds of those 4 transformed points.
- double minX = pts[0];
- double minY = pts[1];
- double maxX = pts[0];
- double maxY = pts[1];
- int i;
- for (i = 1; i < 4; i++) {
- if (pts[2 * i] < minX) {
- minX = pts[2 * i];
- }
- if (pts[2 * i + 1] < minY) {
- minY = pts[2 * i + 1];
- }
- if (pts[2 * i] > maxX) {
- maxX = pts[2 * i];
- }
- if (pts[2 * i + 1] > maxY) {
- maxY = pts[2 * i + 1];
- }
- }
- rect.setRect(minX, minY, maxX - minX, maxY - minY);
- }
-
- /**
- * Populates the SWT rectangle with the provided Swing Rectangle2D's
- * coordinates. Rounding up to the nearest integer.
- *
- * @param aRect awt rectangle to extract coordinates from
- * @param sRect swt rectangle to populate
- */
- public static void awtToSWT(final Rectangle2D aRect, final Rectangle sRect) {
- sRect.x = (int) (aRect.getX() + 0.5);
- sRect.y = (int) (aRect.getY() + 0.5);
- sRect.width = (int) (aRect.getWidth() + 0.5);
- sRect.height = (int) (aRect.getHeight() + 0.5);
- }
-
- /**
- * Converts the provided shape into an array of point coordinates given as
- * one dimensional array with this format: x1,y1,x2,y3,....
- *
- * @param shape shape to convert
- * @return point coordinates given as one dimensional array with this
- * format: x1,y1,x2,y3,...
- */
- public static double[] shapeToPolyline(final Shape shape) {
- segList.clear();
- aPoint.setLocation(0, 0);
-
- final PathIterator pi = shape.getPathIterator(IDENTITY_XFORM, 0.000000001);
- while (!pi.isDone()) {
- final int segType = pi.currentSegment(pts);
- switch (segType) {
- case PathIterator.SEG_MOVETO:
- aPoint.setLocation(pts[0], pts[1]);
- segList.add(new Point2D.Double(pts[0], pts[1]));
- break;
- case PathIterator.SEG_LINETO:
- segList.add(new Point2D.Double(pts[0], pts[1]));
- break;
- case PathIterator.SEG_CLOSE:
- segList.add(new Point2D.Double(aPoint.getX(), aPoint.getY()));
- break;
- default:
- }
- pi.next();
- }
-
- final double[] polyObj = new double[2 * segList.size()];
- for (int i = 0; i < segList.size(); i++) {
- final Point2D p2 = (Point2D) segList.get(i);
- polyObj[2 * i] = (int) (p2.getX() + 0.5);
- polyObj[2 * i + 1] = (int) (p2.getY() + 0.5);
- }
-
- return polyObj;
- }
-
- /**
- * Transforms the given points by the transform provided, leaving the
- * original points untouched.
- *
- * @param points points to transform
- * @param at transform to apply
- * @return transformed coordinates given in format x1,y2,x2,y2,...
- */
- public static int[] transform(final double[] points, final AffineTransform at) {
- final int[] intPts = new int[points.length];
- for (int i = 0; i < points.length / 2; i++) {
- aPoint.setLocation(points[2 * i], points[2 * i + 1]);
- at.transform(aPoint, aPoint);
- intPts[2 * i] = (int) (aPoint.getX() + 0.5);
- intPts[2 * i + 1] = (int) (aPoint.getY() + 0.5);
- }
- return intPts;
- }
-}
diff --git a/swt/src/main/java/edu/umd/cs/piccolox/swt/SWTTimer.java b/swt/src/main/java/edu/umd/cs/piccolox/swt/SWTTimer.java
deleted file mode 100644
index cfb6709..0000000
--- a/swt/src/main/java/edu/umd/cs/piccolox/swt/SWTTimer.java
+++ /dev/null
@@ -1,331 +0,0 @@
-/*
- * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org
- * Copyright (c) 1998-2008, University of Maryland
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification, are permitted provided
- * that the following conditions are met:
- *
- * Redistributions of source code must retain the above copyright notice, this list of conditions
- * and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
- * and the following disclaimer in the documentation and/or other materials provided with the
- * distribution.
- *
- * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its
- * contributors may be used to endorse or promote products derived from this software without specific
- * prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
- * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
- * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package edu.umd.cs.piccolox.swt;
-
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-
-import javax.swing.Timer;
-
-import org.eclipse.swt.widgets.Display;
-
-/**
- * SWT timer.
- *
- * @author Lance Good
- */
-public class SWTTimer extends Timer {
- private static final long serialVersionUID = 1L;
-
- private boolean notify = false;
-
- private int initialDelay;
- private int delay;
- private boolean repeats = true;
- private boolean coalesce = true;
- private Runnable doPostEvent = null;
- private Display display = null;
-
- // These fields are maintained by TimerQueue.
- // eventQueued can also be reset by the TimerQueue, but will only ever
- // happen in applet case when TimerQueues thread is destroyed.
- private long expirationTime;
- private SWTTimer nextTimer;
- boolean running;
-
- /**
- * DoPostEvent is a runnable class that fires actionEvents to the listeners
- * on the EventDispatchThread, via invokeLater.
- *
- * @see #post
- */
- class SWTDoPostEvent implements Runnable {
- public void run() {
- if (notify) {
- fireActionPerformed(new ActionEvent(SWTTimer.this, 0, null, System.currentTimeMillis(), 0));
- if (coalesce) {
- cancelEventOverride();
- }
- }
- }
-
- SWTTimer getTimer() {
- return SWTTimer.this;
- }
- }
-
- /**
- * Constructor for SWTTimer.
- *
- * @param display display associated with this timer
- * @param delay time in milliseconds between firings of this timer
- * @param listener action listener to fire when the timer fires
- */
- public SWTTimer(final Display display, final int delay, final ActionListener listener) {
- super(delay, listener);
- this.delay = delay;
- initialDelay = delay;
-
- doPostEvent = new SWTDoPostEvent();
- this.display = display;
- }
-
- /**
- * Notifies all listeners that have registered interest for notification on
- * this event type.
- *
- * @param e the action event to fire
- */
- protected void fireActionPerformed(final ActionEvent e) {
- // Guaranteed to return a non-null array
- final Object[] listeners = listenerList.getListenerList();
-
- // Process the listeners last to first, notifying
- // those that are interested in this event
- for (int i = listeners.length - 2; i >= 0; i -= 2) {
- if (listeners[i] == ActionListener.class) {
- ((ActionListener) listeners[i + 1]).actionPerformed(e);
- }
- }
- }
-
- /**
- * Returns the timer queue.
- */
- SWTTimerQueue timerQueue() {
- return SWTTimerQueue.sharedInstance(display);
- }
-
- /**
- * Sets the Timer
's delay, the number of milliseconds between
- * successive action events.
- *
- * @param delay the delay in milliseconds
- * @see #setInitialDelay
- */
- public void setDelay(final int delay) {
- if (delay < 0) {
- throw new IllegalArgumentException("Invalid delay: " + delay);
- }
- else {
- this.delay = delay;
- }
- }
-
- /**
- * Returns the delay, in milliseconds, between firings of action events.
- *
- * @see #setDelay
- * @see #getInitialDelay
- * @return delay in milliseconds between firings of this timer
- */
- public int getDelay() {
- return delay;
- }
-
- /**
- * Sets the Timer
's initial delay, which by default is the same
- * as the between-event delay. This is used only for the first action event.
- * Subsequent action events are spaced using the delay property.
- *
- * @param initialDelay the delay, in milliseconds, between the invocation of
- * the start
method and the first action event fired
- * by this timer
- *
- * @see #setDelay
- */
- public void setInitialDelay(final int initialDelay) {
- if (initialDelay < 0) {
- throw new IllegalArgumentException("Invalid initial delay: " + initialDelay);
- }
- else {
- this.initialDelay = initialDelay;
- }
- }
-
- /**
- * Returns the Timer
's initial delay. By default this is the
- * same as the value returned by getDelay.
- *
- * @see #setInitialDelay
- * @see #setDelay
- * @return the initial delay of this timer
- */
- public int getInitialDelay() {
- return initialDelay;
- }
-
- /**
- * If flag
is false
, instructs the
- * Timer
to send only one action event to its listeners.
- *
- * @param flag specify false
to make the timer stop after
- * sending its first action event
- */
- public void setRepeats(final boolean flag) {
- repeats = flag;
- }
-
- /**
- * Returns true
(the default) if the Timer
will
- * send an action event to its listeners multiple times.
- *
- * @see #setRepeats
- * @return true if this timer should repeat when completed
- */
- public boolean isRepeats() {
- return repeats;
- }
-
- /**
- * Sets whether the Timer
coalesces multiple pending
- * ActionEvent
firings. A busy application may not be able to
- * keep up with a Timer
's event generation, causing multiple
- * action events to be queued. When processed, the application sends these
- * events one after the other, causing the Timer
's listeners to
- * receive a sequence of events with no delay between them. Coalescing
- * avoids this situation by reducing multiple pending events to a single
- * event. Timer
s coalesce events by default.
- *
- * @param flag specify false
to turn off coalescing
- */
- public void setCoalesce(final boolean flag) {
- final boolean old = coalesce;
- coalesce = flag;
- if (!old && coalesce) {
- // We must do this as otherwise if the Timer once notified
- // in !coalese mode notify will be stuck to true and never
- // become false.
- cancelEventOverride();
- }
- }
-
- /**
- * Returns true
if the Timer
coalesces multiple
- * pending action events.
- *
- * @see #setCoalesce
- * @return true if this timer coalesces multiple pending action events
- */
- public boolean isCoalesce() {
- return coalesce;
- }
-
- /**
- * Starts the Timer
, causing it to start sending action events
- * to its listeners.
- *
- * @see #stop
- */
- public void start() {
- timerQueue().addTimer(this, System.currentTimeMillis() + getInitialDelay());
- }
-
- /**
- * Returns true
if the Timer
is running.
- *
- * @see #start
- * @return true if this timer is scheduled to run
- */
- public boolean isRunning() {
- return timerQueue().containsTimer(this);
- }
-
- /**
- * Stops the Timer
, causing it to stop sending action events to
- * its listeners.
- *
- * @see #start
- */
- public void stop() {
- timerQueue().removeTimer(this);
- cancelEventOverride();
- }
-
- /**
- * Restarts the Timer
, canceling any pending firings and
- * causing it to fire with its initial delay.
- */
- public void restart() {
- stop();
- start();
- }
-
- /**
- * Resets the internal state to indicate this Timer shouldn't notify any of
- * its listeners. This does not stop a repeatable Timer from firing again,
- * use stop
for that.
- */
- synchronized void cancelEventOverride() {
- notify = false;
- }
-
- synchronized void postOverride() {
- if (!notify || !coalesce) {
- notify = true;
- display.asyncExec(doPostEvent);
- }
- }
-
- /**
- * @param expirationTime the expirationTime to set
- */
- public void setExpirationTime(final long expirationTime) {
- this.expirationTime = expirationTime;
- }
-
- /**
- * @return the expirationTime
- */
- public long getExpirationTime() {
- return expirationTime;
- }
-
- /**
- * @param nextTimer the nextTimer to set
- */
- void setNextTimer(final SWTTimer nextTimer) {
- this.nextTimer = nextTimer;
- }
-
- /**
- * @return the nextTimer
- */
- SWTTimer getNextTimer() {
- return nextTimer;
- }
-
- /**
- * @param running the running to set
- */
- public void setRunning(final boolean running) {
- this.running = running;
- }
-
-}
diff --git a/swt/src/main/java/edu/umd/cs/piccolox/swt/SWTTimerQueue.java b/swt/src/main/java/edu/umd/cs/piccolox/swt/SWTTimerQueue.java
deleted file mode 100644
index 7890108..0000000
--- a/swt/src/main/java/edu/umd/cs/piccolox/swt/SWTTimerQueue.java
+++ /dev/null
@@ -1,381 +0,0 @@
-/*
- * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org
- * Copyright (c) 1998-2008, University of Maryland
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification, are permitted provided
- * that the following conditions are met:
- *
- * Redistributions of source code must retain the above copyright notice, this list of conditions
- * and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
- * and the following disclaimer in the documentation and/or other materials provided with the
- * distribution.
- *
- * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its
- * contributors may be used to endorse or promote products derived from this software without specific
- * prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
- * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
- * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package edu.umd.cs.piccolox.swt;
-
-import org.eclipse.swt.widgets.Display;
-
-/**
- * The SWTTimerQueue is a queue of timers. It has been implemented as a linked
- * list of SWTTimer objects.
- *
- * @author Lance Good
- */
-public class SWTTimerQueue implements Runnable {
- private static SWTTimerQueue instance;
-
- private final Display display;
-
- private SWTTimer firstTimer;
- private boolean running;
-
- /**
- * Creates a timer queue that will be attached the the provided display.
- * It's Timers are expected to modify only this display, or none.
- *
- * @param display the display that will get updated by this queue's timers.
- */
- public SWTTimerQueue(final Display display) {
- this.display = display;
-
- // Now start the TimerQueue thread.
- start();
- }
-
- /**
- * Returns the singleton instance of the SWTTimerQueue. Take note that even
- * when called with different displays it will always return the same result
- * as the first call.
- *
- * @param display display to associate with this Timer Queue's Activities
- * @return singleton instance of SWTTimerQueue
- */
- public static SWTTimerQueue sharedInstance(final Display display) {
- if (instance == null) {
- instance = new SWTTimerQueue(display);
- }
- return instance;
- }
-
- /**
- * Starts the timer queue. If it is already running, a RuntimeException will
- * be thrown.
- */
- synchronized void start() {
- if (running) {
- throw new RuntimeException("Can't start a TimerQueue that is already running");
- }
-
- // Ensures that the Thread will be started from the display thread.
- Display.getDefault().asyncExec(new Runnable() {
- public void run() {
- final Thread timerThread = new Thread(SWTTimerQueue.this, "TimerQueue");
- timerThread.setDaemon(true);
- timerThread.setPriority(Thread.NORM_PRIORITY);
- timerThread.start();
- }
- });
-
- running = true;
- }
-
- /**
- * Stops the TimerQueue Thread.
- */
- synchronized void stop() {
- running = false;
- notifyAll();
- }
-
- /**
- * Adds the provided timer to the queue of scheduled timers.
- *
- * @param timer timer to add
- * @param expirationTime time at which the timer is to be stopped and
- * removed from the queue. Given in unix time.
- */
- synchronized void addTimer(final SWTTimer timer, final long expirationTime) {
- // If the Timer is already in the queue, then do nothing
- if (!timer.isRunning()) {
- insertTimer(timer, expirationTime);
-
- timer.setExpirationTime(expirationTime);
-
- timer.setRunning(true);
- notifyAll();
- }
- }
-
- /**
- * Insert the Timer into the queue in the order they will expire. If
- * multiple timers are set to expire at the same time, it will insert it
- * after the last one; that way they expire in the order they came in.
- *
- * @param timer timer to insert into the queue
- * @param expirationTime time in UNIX time at which the new timer should
- * expire
- */
- private void insertTimer(final SWTTimer timer, final long expirationTime) {
- SWTTimer previousTimer = findLastTimerExpiringBefore(expirationTime);
- if (previousTimer == null) {
- firstTimer = timer;
- }
- else {
- timer.setNextTimer(previousTimer.getNextTimer());
- previousTimer.setNextTimer(timer);
- }
- }
-
- /**
- * Finds the last timer that will expire before or at the given expiration
- * time. If there are multiple timers expiring at the same time, the last
- * one in the queue will be returned.
- *
- * @param expirationTime expiration to compare against timers in the queue
- * @return last timer that will expire before or at the given expiration
- * time
- */
- private SWTTimer findLastTimerExpiringBefore(final long expirationTime) {
- SWTTimer previousTimer = null;
- SWTTimer nextTimer = firstTimer;
-
- while (nextTimer != null && nextTimer.getExpirationTime() > expirationTime) {
- previousTimer = nextTimer;
- nextTimer = nextTimer.getNextTimer();
- }
-
- return previousTimer;
-
- }
-
- /**
- * Removes the provided timer from the Timer Queue. If it is not found, then
- * nothing happens.
- *
- * @param timer timer to remove from the queue
- */
- synchronized void removeTimer(final SWTTimer timer) {
- if (!timer.isRunning()) {
- return;
- }
-
- if (timer == firstTimer) {
- firstTimer = timer.getNextTimer();
- }
- else {
- SWTTimer previousTimer = findLastTimerBefore(timer);
- if (previousTimer != null) {
- previousTimer.setNextTimer(timer.getNextTimer());
- }
- }
-
- timer.setExpirationTime(0);
- timer.setNextTimer(null);
- timer.setRunning(false);
- }
-
- /**
- * Finds the timer that immediately precedes the provided timer in the
- * queue.
- *
- * @param timer to search for
- * @return timer immediately preceding found timer, or null if not found
- */
- private SWTTimer findLastTimerBefore(final SWTTimer timer) {
- SWTTimer previousTimer = null;
- SWTTimer currentTimer = firstTimer;
-
- while (currentTimer != null) {
- if (currentTimer == timer) {
- return previousTimer;
- }
-
- previousTimer = currentTimer;
- currentTimer = currentTimer.getNextTimer();
- }
-
- return null;
- }
-
- /**
- * Returns true if this timer queue contains the given timer.
- *
- * @param timer timer being checked
- * @return true if timer is scheduled in this queue
- */
- synchronized boolean containsTimer(final SWTTimer timer) {
- // TODO: making this use isRunning without causing an infinite loop
- return timer.running;
- }
-
- /**
- * If there are a ton of timers, this method may never return. It loops
- * checking to see if the head of the Timer list has expired. If it has, it
- * posts the Timer and reschedules it if necessary.
- *
- * @return how long the app can take before it should invoke this method
- * again.
- */
- private synchronized long postExpiredTimers() {
- SWTTimer timer;
- long currentTime;
- long timeToWait;
-
- // The timeToWait we return should never be negative and only be zero
- // when we have no Timers to wait for.
-
- do {
- timer = firstTimer;
- if (timer == null) {
- return 0;
- }
-
- currentTime = System.currentTimeMillis();
- timeToWait = timer.getExpirationTime() - currentTime;
-
- if (timeToWait <= 0) {
- try {
- timer.postOverride(); // have timer post an event
- }
- catch (final SecurityException e) {
- throw new RuntimeException("Could not post event", e);
- }
-
- // Remove the timer from the queue
- removeTimer(timer);
-
- // This tries to keep the interval uniform at
- // the cost of drift.
- if (timer.isRepeats()) {
- addTimer(timer, currentTime + timer.getDelay());
- }
-
- // Allow other threads to call addTimer() and removeTimer()
- // even when we are posting Timers like mad. Since the wait()
- // releases the lock, be sure not to maintain any state
- // between iterations of the loop.
-
- try {
- wait(1);
- }
- catch (final InterruptedException e) {
- // Nothing to do
- }
- }
- } while (timeToWait <= 0);
-
- return timeToWait;
- }
-
- /**
- * Dispatches work to timers until the queue is told to stop running.
- */
- public synchronized void run() {
- long timeToWait;
-
- try {
- while (running) {
- timeToWait = postExpiredTimers();
- try {
- wait(timeToWait);
- }
- catch (final InterruptedException e) {
- // Nothing to do
- }
- }
- }
- catch (final ThreadDeath td) {
- running = false;
- // Mark all the timers we contain as not being queued.
- SWTTimer timer = firstTimer;
- while (timer != null) {
- timer.cancelEventOverride();
- timer = timer.getNextTimer();
- }
- display.asyncExec(new SWTTimerQueueRestart(display));
- throw td;
- }
- }
-
- /**
- * Generates a string handy for debugging the contents of the timer queue.
- *
- * @return String representation of the queue for use in debugging
- */
- public synchronized String toString() {
- StringBuffer buf;
- SWTTimer nextTimer;
-
- buf = new StringBuffer();
- buf.append("TimerQueue (");
-
- nextTimer = firstTimer;
- while (nextTimer != null) {
- buf.append(nextTimer.toString());
-
- nextTimer = nextTimer.getNextTimer();
- if (nextTimer != null) {
- buf.append(", ");
- }
- }
-
- buf.append(")");
- return buf.toString();
- }
-
- /**
- * Runnable that will message the shared instance of the Timer Queue to
- * restart.
- */
- protected static class SWTTimerQueueRestart implements Runnable {
- /** Tracks whether a restart has been attempted. */
- private boolean attemptedStart;
-
- private final Display display;
-
- /**
- * Constructs a QueueRestart Runnable that will message the Timer Queue
- * to Restart.
- *
- * @param display display associated with the SWTTimerQueue
- */
- public SWTTimerQueueRestart(final Display display) {
- this.display = display;
- }
-
- /**
- * Attempts to restart the queue associated with the display.
- */
- public synchronized void run() {
- if (attemptedStart) {
- return;
- }
-
- final SWTTimerQueue q = SWTTimerQueue.sharedInstance(display);
-
- synchronized (q) {
- if (!q.running) {
- q.start();
- }
- }
-
- attemptedStart = true;
- }
- }
-
-}
diff --git a/swt/src/main/java/edu/umd/cs/piccolox/swt/package.html b/swt/src/main/java/edu/umd/cs/piccolox/swt/package.html
deleted file mode 100644
index 1af36f5..0000000
--- a/swt/src/main/java/edu/umd/cs/piccolox/swt/package.html
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
This package provides a SWT implementation of the core Piccolo library.
- - diff --git a/swt/src/main/java/org/piccolo2d/extras/swt/PSWTBoundsHandle.java b/swt/src/main/java/org/piccolo2d/extras/swt/PSWTBoundsHandle.java new file mode 100644 index 0000000..981f0de --- /dev/null +++ b/swt/src/main/java/org/piccolo2d/extras/swt/PSWTBoundsHandle.java @@ -0,0 +1,430 @@ +/* + * Copyright (c) 2008-2010, 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.extras.swt; + +import java.awt.Cursor; +import java.awt.geom.Point2D; +import java.util.ArrayList; +import java.util.Iterator; + +import javax.swing.SwingConstants; + +import org.piccolo2d.PCamera; +import org.piccolo2d.PNode; +import org.piccolo2d.event.PBasicInputEventHandler; +import org.piccolo2d.event.PInputEvent; +import org.piccolo2d.extras.util.PBoundsLocator; +import org.piccolo2d.util.PBounds; +import org.piccolo2d.util.PDimension; +import org.piccolo2d.util.PPickPath; + + +/** + * PSWTBoundsHandle a handle for resizing the bounds of another node. If a + * bounds handle is dragged such that the other node's width or height becomes + * negative then the each drag handle's locator assciated with that other node + * is "flipped" so that they are attached to and dragging a different corner of + * the nodes bounds. + * + * @version 1.0 + * @author Jesse Grosjean + */ +public class PSWTBoundsHandle extends PSWTHandle { + private final class HandleCursorEventHandler extends PBasicInputEventHandler { + boolean cursorPushed = false; + + public void mouseEntered(final PInputEvent aEvent) { + if (!cursorPushed) { + aEvent.pushCursor(getCursorFor(((PBoundsLocator) getLocator()).getSide())); + cursorPushed = true; + } + } + + public void mouseExited(final PInputEvent aEvent) { + final PPickPath focus = aEvent.getInputManager().getMouseFocus(); + + if (cursorPushed && isNewFocus(focus)) { + aEvent.popCursor(); + cursorPushed = false; + } + } + + private boolean isNewFocus(final PPickPath focus) { + return (focus == null || focus.getPickedNode() != PSWTBoundsHandle.this); + } + + public void mouseReleased(final PInputEvent event) { + if (cursorPushed) { + event.popCursor(); + cursorPushed = false; + } + } + } + + private static final long serialVersionUID = 1L; + private PBasicInputEventHandler handleCursorHandler; + + /** + * Adds bounds handles to all corners and edges of the provided node. + * + * @param node to decorate with bounds handles. + */ + public static void addBoundsHandlesTo(final PNode node) { + node.addChild(new PSWTBoundsHandle(PBoundsLocator.createEastLocator(node))); + node.addChild(new PSWTBoundsHandle(PBoundsLocator.createWestLocator(node))); + node.addChild(new PSWTBoundsHandle(PBoundsLocator.createNorthLocator(node))); + node.addChild(new PSWTBoundsHandle(PBoundsLocator.createSouthLocator(node))); + node.addChild(new PSWTBoundsHandle(PBoundsLocator.createNorthEastLocator(node))); + node.addChild(new PSWTBoundsHandle(PBoundsLocator.createNorthWestLocator(node))); + node.addChild(new PSWTBoundsHandle(PBoundsLocator.createSouthEastLocator(node))); + node.addChild(new PSWTBoundsHandle(PBoundsLocator.createSouthWestLocator(node))); + } + + /** + * Adds sticky bounds handles to all corners and edges of the provided node + * and for display on the provided camera. + * + * @param node to decorate with bounds handles. + * @param camera camera onto which the handles should be stuck + */ + public static void addStickyBoundsHandlesTo(final PNode node, final PCamera camera) { + camera.addChild(new PSWTBoundsHandle(PBoundsLocator.createEastLocator(node))); + camera.addChild(new PSWTBoundsHandle(PBoundsLocator.createWestLocator(node))); + camera.addChild(new PSWTBoundsHandle(PBoundsLocator.createNorthLocator(node))); + camera.addChild(new PSWTBoundsHandle(PBoundsLocator.createSouthLocator(node))); + camera.addChild(new PSWTBoundsHandle(PBoundsLocator.createNorthEastLocator(node))); + camera.addChild(new PSWTBoundsHandle(PBoundsLocator.createNorthWestLocator(node))); + camera.addChild(new PSWTBoundsHandle(PBoundsLocator.createSouthEastLocator(node))); + camera.addChild(new PSWTBoundsHandle(PBoundsLocator.createSouthWestLocator(node))); + } + + /** + * Removes all bounds handles from the specified node. + * + * @param node node from which to remove bounds handles + */ + public static void removeBoundsHandlesFrom(final PNode node) { + final ArrayList handles = new ArrayList(); + + final Iterator i = node.getChildrenIterator(); + while (i.hasNext()) { + final PNode each = (PNode) i.next(); + if (each instanceof PSWTBoundsHandle) { + handles.add(each); + } + } + node.removeChildren(handles); + } + + /** + * Creates a bounds handle that will use the provided bounds locator to + * position itself. + * + * @param locator locator to use when positioning this handle + */ + public PSWTBoundsHandle(final PBoundsLocator locator) { + super(locator); + } + + /** + * Installs handlers responsible for updating the attached node's bounds and + * for updating the cursor when the mous enters a handle. + */ + protected void installHandleEventHandlers() { + super.installHandleEventHandlers(); + handleCursorHandler = new HandleCursorEventHandler(); + addInputEventListener(handleCursorHandler); + } + + /** + * Return the event handler that is responsible for setting the mouse cursor + * when it enters/exits this handle. + * + * @return handler responsible for keeping the mouse cursor up to date + */ + public PBasicInputEventHandler getHandleCursorEventHandler() { + return handleCursorHandler; + } + + /** + * Callback invoked when the user has started to drag a handle. + * + * @param aLocalPoint point in the handle's coordinate system at which the + * drag was started + * @param aEvent Piccolo2d Event representing the start of the drag + */ + public void startHandleDrag(final Point2D aLocalPoint, final PInputEvent aEvent) { + final PBoundsLocator l = (PBoundsLocator) getLocator(); + l.getNode().startResizeBounds(); + } + + /** + * Callback invoked when the user is dragging the handle. Updates the + * associated node appropriately. + * + * @param aLocalDimension magnitude of drag in the handle's coordinate + * system + * @param aEvent Piccolo2d Event representing the start of the drag + */ + public void dragHandle(final PDimension aLocalDimension, final PInputEvent aEvent) { + final PBoundsLocator l = (PBoundsLocator) getLocator(); + + final PNode n = l.getNode(); + final PBounds b = n.getBounds(); + + final PNode parent = getParent(); + if (parent != n && parent instanceof PCamera) { + ((PCamera) parent).localToView(aLocalDimension); + } + + localToGlobal(aLocalDimension); + n.globalToLocal(aLocalDimension); + + final double dx = aLocalDimension.getWidth(); + final double dy = aLocalDimension.getHeight(); + + switch (l.getSide()) { + case SwingConstants.NORTH: + b.setRect(b.x, b.y + dy, b.width, b.height - dy); + break; + + case SwingConstants.SOUTH: + b.setRect(b.x, b.y, b.width, b.height + dy); + break; + + case SwingConstants.EAST: + b.setRect(b.x, b.y, b.width + dx, b.height); + break; + + case SwingConstants.WEST: + b.setRect(b.x + dx, b.y, b.width - dx, b.height); + break; + + case SwingConstants.NORTH_WEST: + b.setRect(b.x + dx, b.y + dy, b.width - dx, b.height - dy); + break; + + case SwingConstants.SOUTH_WEST: + b.setRect(b.x + dx, b.y, b.width - dx, b.height + dy); + break; + + case SwingConstants.NORTH_EAST: + b.setRect(b.x, b.y + dy, b.width + dx, b.height - dy); + break; + + case SwingConstants.SOUTH_EAST: + b.setRect(b.x, b.y, b.width + dx, b.height + dy); + break; + default: + // Leave bounds untouched + } + + boolean flipX = false; + boolean flipY = false; + + if (b.width < 0) { + flipX = true; + b.width = -b.width; + b.x -= b.width; + } + + if (b.height < 0) { + flipY = true; + b.height = -b.height; + b.y -= b.height; + } + + if (flipX || flipY) { + flipSiblingBoundsHandles(flipX, flipY); + } + + n.setBounds(b); + } + + /** + * Callback invoked when the handle stops being dragged. + * + * @param aLocalPoint point in the handle's coordinate system at which the + * drag was stopped + * @param aEvent Piccolo2d Event representing the stop of the drag + */ + public void endHandleDrag(final Point2D aLocalPoint, final PInputEvent aEvent) { + final PBoundsLocator l = (PBoundsLocator) getLocator(); + l.getNode().endResizeBounds(); + } + + /** + * Iterates over all of this node's handles flipping them if necessary. This + * is needed since a node can become inverted when it's width or height + * becomes negative. + * + * @param flipX whether to allow flipping in the horizontal direction + * @param flipY whether to allow flipping in the vertical direction + */ + public void flipSiblingBoundsHandles(final boolean flipX, final boolean flipY) { + final Iterator i = getParent().getChildrenIterator(); + while (i.hasNext()) { + final Object each = i.next(); + if (each instanceof PSWTBoundsHandle) { + ((PSWTBoundsHandle) each).flipHandleIfNeeded(flipX, flipY); + } + } + } + + /** + * Flips this particular handle around if needed. This is necessary since a + * node can become inverted when it's width or height becomes negative. + * + * @param flipX whether to allow flipping in the horizontal direction + * @param flipY whether to allow flipping in the vertical direction + */ + public void flipHandleIfNeeded(final boolean flipX, final boolean flipY) { + if (!flipX && !flipY) { + return; + } + + final PBoundsLocator l = (PBoundsLocator) getLocator(); + switch (l.getSide()) { + case SwingConstants.NORTH: + if (flipY) { + l.setSide(SwingConstants.SOUTH); + } + break; + + case SwingConstants.SOUTH: + if (flipY) { + l.setSide(SwingConstants.NORTH); + } + break; + + case SwingConstants.EAST: + if (flipX) { + l.setSide(SwingConstants.WEST); + } + break; + + case SwingConstants.WEST: + if (flipX) { + l.setSide(SwingConstants.EAST); + } + break; + + case SwingConstants.NORTH_WEST: + if (flipX && flipY) { + l.setSide(SwingConstants.SOUTH_EAST); + } + else if (flipX) { + l.setSide(SwingConstants.NORTH_EAST); + } + else if (flipY) { + l.setSide(SwingConstants.SOUTH_WEST); + } + break; + + case SwingConstants.SOUTH_WEST: + if (flipX && flipY) { + l.setSide(SwingConstants.NORTH_EAST); + } + else if (flipX) { + l.setSide(SwingConstants.SOUTH_EAST); + } + else if (flipY) { + l.setSide(SwingConstants.NORTH_WEST); + } + break; + + case SwingConstants.NORTH_EAST: + if (flipX && flipY) { + l.setSide(SwingConstants.SOUTH_WEST); + } + else if (flipX) { + l.setSide(SwingConstants.NORTH_WEST); + } + else if (flipY) { + l.setSide(SwingConstants.SOUTH_EAST); + } + break; + + case SwingConstants.SOUTH_EAST: + if (flipX && flipY) { + l.setSide(SwingConstants.NORTH_WEST); + } + else if (flipX) { + l.setSide(SwingConstants.SOUTH_WEST); + } + else if (flipY) { + l.setSide(SwingConstants.NORTH_EAST); + } + break; + default: + // Do nothing + } + + // reset locator to update layout + setLocator(l); + } + + /** + * Returns an appropriate cursor to display when the mouse is over a handle + * on the side provided. + * + * @param side value from SwingConstants + * + * @return Appropriate cursor, or null if no appropriate cursor can be found + */ + public Cursor getCursorFor(final int side) { + switch (side) { + case SwingConstants.NORTH: + return new Cursor(Cursor.N_RESIZE_CURSOR); + + case SwingConstants.SOUTH: + return new Cursor(Cursor.S_RESIZE_CURSOR); + + case SwingConstants.EAST: + return new Cursor(Cursor.E_RESIZE_CURSOR); + + case SwingConstants.WEST: + return new Cursor(Cursor.W_RESIZE_CURSOR); + + case SwingConstants.NORTH_WEST: + return new Cursor(Cursor.NW_RESIZE_CURSOR); + + case SwingConstants.SOUTH_WEST: + return new Cursor(Cursor.SW_RESIZE_CURSOR); + + case SwingConstants.NORTH_EAST: + return new Cursor(Cursor.NE_RESIZE_CURSOR); + + case SwingConstants.SOUTH_EAST: + return new Cursor(Cursor.SE_RESIZE_CURSOR); + default: + return null; + } + } +} diff --git a/swt/src/main/java/org/piccolo2d/extras/swt/PSWTCanvas.java b/swt/src/main/java/org/piccolo2d/extras/swt/PSWTCanvas.java new file mode 100644 index 0000000..9e2f59c --- /dev/null +++ b/swt/src/main/java/org/piccolo2d/extras/swt/PSWTCanvas.java @@ -0,0 +1,738 @@ +/* + * Copyright (c) 2008-2010, 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.extras.swt; + +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.event.InputEvent; +import java.awt.geom.Rectangle2D; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.events.KeyListener; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.MouseListener; +import org.eclipse.swt.events.MouseMoveListener; +import org.eclipse.swt.events.PaintEvent; +import org.eclipse.swt.events.PaintListener; +import org.eclipse.swt.graphics.Cursor; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Composite; +import org.piccolo2d.PCamera; +import org.piccolo2d.PComponent; +import org.piccolo2d.PLayer; +import org.piccolo2d.PRoot; +import org.piccolo2d.event.PInputEventListener; +import org.piccolo2d.event.PPanEventHandler; +import org.piccolo2d.event.PZoomEventHandler; +import org.piccolo2d.util.PBounds; +import org.piccolo2d.util.PDebug; +import org.piccolo2d.util.PPaintContext; +import org.piccolo2d.util.PStack; + + +/** + * PSWTCanvas is an SWT Composite that can be used to embed + * Piccolo into a SWT application. Canvases view the Piccolo scene graph + * through a camera. The canvas manages screen updates coming from this camera, + * and forwards swing mouse and keyboard events to the camera. + * + * @version 1.0 + * @author Jesse Grosjean + */ +public class PSWTCanvas extends Composite implements PComponent { + private static final int SWT_BUTTON1 = 1; + private static final int SWT_BUTTON2 = 2; + private static final int SWT_BUTTON3 = 3; + + /** + * Terrible Singleton instance of the PSWTCanvas. Falsely assumes you will + * only have one of these per application. + */ + public static PSWTCanvas CURRENT_CANVAS = null; + + private Image backBuffer; + private boolean doubleBuffered = true; + private PCamera camera; + private final PStack cursorStack; + private Cursor curCursor; + private int interacting; + private int defaultRenderQuality; + private int animatingRenderQuality; + private int interactingRenderQuality; + private final PPanEventHandler panEventHandler; + private final PZoomEventHandler zoomEventHandler; + private boolean paintingImmediately; + private boolean animatingOnLastPaint; + + private boolean isButton1Pressed; + private boolean isButton2Pressed; + private boolean isButton3Pressed; + + /** + * Construct a canvas with the basic scene graph consisting of a root, + * camera, and layer. Event handlers for zooming and panning are + * automatically installed. + * + * @param parent component onto which the canvas is installed + * @param style component style for the PSWTCanvas + */ + public PSWTCanvas(final Composite parent, final int style) { + super(parent, style | SWT.NO_BACKGROUND | SWT.NO_REDRAW_RESIZE); + + CURRENT_CANVAS = this; + cursorStack = new PStack(); + setCamera(createBasicSceneGraph()); + installInputSources(); + setDefaultRenderQuality(PPaintContext.HIGH_QUALITY_RENDERING); + setAnimatingRenderQuality(PPaintContext.LOW_QUALITY_RENDERING); + setInteractingRenderQuality(PPaintContext.LOW_QUALITY_RENDERING); + panEventHandler = new PPanEventHandler(); + zoomEventHandler = new PZoomEventHandler(); + addInputEventListener(panEventHandler); + addInputEventListener(zoomEventHandler); + + installPaintListener(); + installDisposeListener(); + } + + private void installPaintListener() { + addPaintListener(new PaintListener() { + public void paintControl(final PaintEvent pe) { + paintComponent(pe.gc, pe.x, pe.y, pe.width, pe.height); + } + }); + } + + private void installDisposeListener() { + SWTGraphics2D.incrementGCCount(); + addDisposeListener(new DisposeListener() { + public void widgetDisposed(final DisposeEvent de) { + getRoot().getActivityScheduler().removeAllActivities(); + SWTGraphics2D.decrementGCCount(); + } + }); + } + + // **************************************************************** + // Basic - Methods for accessing common Piccolo2D nodes. + // **************************************************************** + + /** + * Get the pan event handler associated with this canvas. This event handler + * is set up to get events from the camera associated with this canvas by + * default. + * + * @return the current pan event handler, which may be null + */ + public PPanEventHandler getPanEventHandler() { + return panEventHandler; + } + + /** + * Get the zoom event handler associated with this canvas. This event + * handler is set up to get events from the camera associated with this + * canvas by default. + * + * @return the event handler installed to handle zooming + */ + public PZoomEventHandler getZoomEventHandler() { + return zoomEventHandler; + } + + /** + * Return the camera associated with this canvas. All input events from this + * canvas go through this camera. And this is the camera that paints this + * canvas. + * + * @return the camera associated with this canvas + */ + public PCamera getCamera() { + return camera; + } + + /** + * Set the camera associated with this canvas. All input events from this + * canvas go through this camera. And this is the camera that paints this + * canvas. + * + * @param newCamera camera to attach to this canvas + */ + public void setCamera(final PCamera newCamera) { + if (camera != null) { + camera.setComponent(null); + } + + camera = newCamera; + + if (camera != null) { + camera.setComponent(this); + + final Rectangle swtRect = getBounds(); + + camera.setBounds(new Rectangle2D.Double(swtRect.x, swtRect.y, swtRect.width, swtRect.height)); + } + } + + /** + * Return root for this canvas. + * + * @return root of the scene this canvas is viewing through its camera + */ + public PRoot getRoot() { + return camera.getRoot(); + } + + /** + * Helper method to return the first layer attached to the camera of this + * canvas. + * + * Short form ofcanvas.getCamera.getLayer(0)
+ *
+ * @return the first layer attached to the camera of this canvas
+ */
+ public PLayer getLayer() {
+ return camera.getLayer(0);
+ }
+
+ /**
+ * Add an input listener to the camera associated with this canvas.
+ *
+ * @param listener listener to add to to the camera
+ */
+ public void addInputEventListener(final PInputEventListener listener) {
+ getCamera().addInputEventListener(listener);
+ }
+
+ /**
+ * Remove an input listener to the camera associated with this canvas. Does
+ * nothign is the listener is not found.
+ *
+ * @param listener listener to remove from the set of event listeners
+ * attached to this canvas.
+ */
+ public void removeInputEventListener(final PInputEventListener listener) {
+ getCamera().removeInputEventListener(listener);
+ }
+
+ /**
+ * Builds the basic scene graph associated with this canvas. Developers may
+ * override this method to install their own layers, and cameras.
+ *
+ * @return PCamera viewing the freshly created scene
+ */
+ public PCamera createBasicSceneGraph() {
+ final PRoot r = new PSWTRoot(this);
+ final PLayer l = new PLayer();
+ final PCamera c = new PCamera();
+
+ r.addChild(c);
+ r.addChild(l);
+ c.addLayer(l);
+
+ return c;
+ }
+
+ // ****************************************************************
+ // Painting
+ // ****************************************************************
+
+ /**
+ * Return true if this canvas has been marked as interacting. If so the
+ * canvas will normally render at a lower quality that is faster.
+ *
+ * @return true if canvas is flagged as interacting
+ */
+ public boolean getInteracting() {
+ return interacting > 0;
+ }
+
+ /**
+ * Return true if any activities that respond with true to the method
+ * isAnimating were run in the last PRoot.processInputs() loop. This values
+ * is used by this canvas to determine the render quality to use for the
+ * next paint.
+ *
+ * @return true if there is an animating activity that is currently active
+ */
+ public boolean getAnimating() {
+ return getRoot().getActivityScheduler().getAnimating();
+ }
+
+ /**
+ * Changes the number of callers that are interacting with the canvas. Will
+ * allow the scene to be rendered in a lower quality if the number is not 0.
+ *
+ * @param isInteracting state the client considers the PSWTCanvas to be in
+ * with regard to interacting
+ */
+ public void setInteracting(final boolean isInteracting) {
+ if (isInteracting) {
+ interacting++;
+ }
+ else {
+ interacting--;
+ }
+
+ if (!getInteracting()) {
+ repaint();
+ }
+ }
+
+ /**
+ * Get whether this canvas should use double buffering - the default is to
+ * double buffer.
+ *
+ * @return true if double buffering is enabled
+ */
+ public boolean getDoubleBuffered() {
+ return doubleBuffered;
+ }
+
+ /**
+ * Set whether this canvas should use double buffering - the default is yes.
+ *
+ * @param doubleBuffered value of double buffering flas
+ */
+ public void setDoubleBuffered(final boolean doubleBuffered) {
+ this.doubleBuffered = doubleBuffered;
+ if (!doubleBuffered && backBuffer != null) {
+ backBuffer.dispose();
+ backBuffer = null;
+ }
+ }
+
+ /**
+ * Set the render quality that should be used when rendering this canvas.
+ * The default value is PPaintContext.HIGH_QUALITY_RENDERING.
+ *
+ * @param requestedQuality supports PPaintContext.HIGH_QUALITY_RENDERING or
+ * PPaintContext.LOW_QUALITY_RENDERING
+ */
+ public void setDefaultRenderQuality(final int requestedQuality) {
+ defaultRenderQuality = requestedQuality;
+ repaint();
+ }
+
+ /**
+ * Set the render quality that should be used when rendering this canvas
+ * when it is animating. The default value is
+ * PPaintContext.LOW_QUALITY_RENDERING.
+ *
+ * @param requestedQuality supports PPaintContext.HIGH_QUALITY_RENDERING or
+ * PPaintContext.LOW_QUALITY_RENDERING
+ */
+ public void setAnimatingRenderQuality(final int requestedQuality) {
+ animatingRenderQuality = requestedQuality;
+ repaint();
+ }
+
+ /**
+ * Set the render quality that should be used when rendering this canvas
+ * when it is interacting. The default value is
+ * PPaintContext.LOW_QUALITY_RENDERING.
+ *
+ * @param requestedQuality supports PPaintContext.HIGH_QUALITY_RENDERING or
+ * PPaintContext.LOW_QUALITY_RENDERING
+ */
+ public void setInteractingRenderQuality(final int requestedQuality) {
+ interactingRenderQuality = requestedQuality;
+ repaint();
+ }
+
+ /**
+ * Set the canvas cursor, and remember the previous cursor on the cursor
+ * stack. Under the hood it is mapping the java.awt.Cursor to
+ * org.eclipse.swt.graphics.Cursor objects.
+ *
+ * @param newCursor new cursor to push onto the cursor stack
+ */
+ public void pushCursor(final java.awt.Cursor newCursor) {
+ Cursor swtCursor = null;
+ if (newCursor.getType() == java.awt.Cursor.N_RESIZE_CURSOR) {
+ swtCursor = new Cursor(getDisplay(), SWT.CURSOR_SIZEN);
+ }
+ else if (newCursor.getType() == java.awt.Cursor.NE_RESIZE_CURSOR) {
+ swtCursor = new Cursor(getDisplay(), SWT.CURSOR_SIZENE);
+ }
+ else if (newCursor.getType() == java.awt.Cursor.NW_RESIZE_CURSOR) {
+ swtCursor = new Cursor(getDisplay(), SWT.CURSOR_SIZENW);
+ }
+ else if (newCursor.getType() == java.awt.Cursor.S_RESIZE_CURSOR) {
+ swtCursor = new Cursor(getDisplay(), SWT.CURSOR_SIZES);
+ }
+ else if (newCursor.getType() == java.awt.Cursor.SE_RESIZE_CURSOR) {
+ swtCursor = new Cursor(getDisplay(), SWT.CURSOR_SIZESE);
+ }
+ else if (newCursor.getType() == java.awt.Cursor.SW_RESIZE_CURSOR) {
+ swtCursor = new Cursor(getDisplay(), SWT.CURSOR_SIZESW);
+ }
+ else if (newCursor.getType() == java.awt.Cursor.E_RESIZE_CURSOR) {
+ swtCursor = new Cursor(getDisplay(), SWT.CURSOR_SIZEE);
+ }
+ else if (newCursor.getType() == java.awt.Cursor.W_RESIZE_CURSOR) {
+ swtCursor = new Cursor(getDisplay(), SWT.CURSOR_SIZEW);
+ }
+ else if (newCursor.getType() == java.awt.Cursor.TEXT_CURSOR) {
+ swtCursor = new Cursor(getDisplay(), SWT.CURSOR_IBEAM);
+ }
+ else if (newCursor.getType() == java.awt.Cursor.HAND_CURSOR) {
+ swtCursor = new Cursor(getDisplay(), SWT.CURSOR_HAND);
+ }
+ else if (newCursor.getType() == java.awt.Cursor.MOVE_CURSOR) {
+ swtCursor = new Cursor(getDisplay(), SWT.CURSOR_SIZEALL);
+ }
+ else if (newCursor.getType() == java.awt.Cursor.CROSSHAIR_CURSOR) {
+ swtCursor = new Cursor(getDisplay(), SWT.CURSOR_CROSS);
+ }
+ else if (newCursor.getType() == java.awt.Cursor.WAIT_CURSOR) {
+ swtCursor = new Cursor(getDisplay(), SWT.CURSOR_WAIT);
+ }
+
+ if (swtCursor != null) {
+ if (curCursor != null) {
+ cursorStack.push(curCursor);
+ }
+ curCursor = swtCursor;
+ setCursor(swtCursor);
+ }
+ }
+
+ /**
+ * Pop the cursor on top of the cursorStack and set it as the canvas cursor.
+ */
+ public void popCursor() {
+ if (curCursor != null) {
+ // We must manually dispose of cursors under SWT
+ curCursor.dispose();
+ }
+
+ if (cursorStack.isEmpty()) {
+ curCursor = null;
+ }
+ else {
+ curCursor = (Cursor) cursorStack.pop();
+ }
+
+ // This sets the cursor back to default
+ setCursor(curCursor);
+ }
+
+ // ****************************************************************
+ // Code to manage connection to Swing. There appears to be a bug in
+ // swing where it will occasionally send to many mouse pressed or mouse
+ // released events. Below we attempt to filter out those cases before
+ // they get delivered to the Piccolo2D framework.
+ // ****************************************************************
+
+ /**
+ * This method installs mouse and key listeners on the canvas that forward
+ * those events to Piccolo2D.
+ */
+ protected void installInputSources() {
+ MouseInputSource mouseInputSource = new MouseInputSource();
+ addMouseListener(mouseInputSource);
+ addMouseMoveListener(mouseInputSource);
+
+ addKeyListener(new KeyboardInputSource());
+ }
+
+ /**
+ * Dispatches the given event to the default input manager for the root of
+ * this canvas.
+ *
+ * @param awtEvent awt event needing dispatching
+ * @param type type of the event
+ */
+ protected void sendInputEventToInputManager(final InputEvent awtEvent, final int type) {
+ getRoot().getDefaultInputManager().processEventFromCamera(awtEvent, type, getCamera());
+ }
+
+ /**
+ * Changes the bounds of this PSWTCanvas. Updating the camera and the double
+ * buffered image appropriately.
+ *
+ * @param x left of the new bounds
+ * @param y top of the new bounds
+ * @param newWidth new width of the bounds
+ * @param newHeight new height of the bounds
+ */
+ public void setBounds(final int x, final int y, final int newWidth, final int newHeight) {
+ camera.setBounds(camera.getX(), camera.getY(), newWidth, newHeight);
+
+ if (backBufferNeedsResizing(newWidth, newHeight)) {
+ resizeBackBuffer(newWidth, newHeight);
+ }
+
+ super.setBounds(x, y, newWidth, newHeight);
+ }
+
+ private void resizeBackBuffer(final int newWidth, final int newHeight) {
+ if (backBuffer != null) {
+ backBuffer.dispose();
+ }
+ backBuffer = new Image(getDisplay(), newWidth, newHeight);
+ }
+
+ private boolean backBufferNeedsResizing(final int newWidth, final int newHeight) {
+ if (!doubleBuffered) {
+ return false;
+ }
+
+ if (backBuffer == null) {
+ return true;
+ }
+
+ return backBuffer.getBounds().width < newWidth || backBuffer.getBounds().height < newHeight;
+ }
+
+ /**
+ * Exists to dispatch from the Swing's repaint method to SWT's redraw
+ * method.
+ */
+ public void repaint() {
+ super.redraw();
+ }
+
+ /**
+ * Flags the bounds provided as needing to be redrawn.
+ *
+ * @param bounds the bounds that should be repainted
+ */
+ public void repaint(final PBounds bounds) {
+ bounds.expandNearestIntegerDimensions();
+ bounds.inset(-1, -1);
+
+ redraw((int) bounds.x, (int) bounds.y, (int) bounds.width, (int) bounds.height, true);
+ }
+
+ /**
+ * Paints the region specified of the canvas onto the given Graphics
+ * Context.
+ *
+ * @param gc graphics onto within painting should occur
+ * @param x left of the dirty region
+ * @param y top of the dirty region
+ * @param w width of the dirty region
+ * @param h height of the dirty region
+ */
+ public void paintComponent(final GC gc, final int x, final int y, final int w, final int h) {
+ PDebug.startProcessingOutput();
+
+ GC imageGC = null;
+ Graphics2D g2 = null;
+ if (doubleBuffered) {
+ imageGC = new GC(backBuffer);
+ g2 = new SWTGraphics2D(imageGC, getDisplay());
+ }
+ else {
+ g2 = new SWTGraphics2D(gc, getDisplay());
+ }
+
+ g2.setColor(Color.white);
+ g2.setBackground(Color.white);
+
+ final Rectangle rect = getBounds();
+ g2.fillRect(0, 0, rect.width, rect.height);
+
+ // This fixes a problem with standard debugging of region management in
+ // SWT
+ if (PDebug.debugRegionManagement) {
+ final Rectangle r = gc.getClipping();
+ final Rectangle2D r2 = new Rectangle2D.Double(r.x, r.y, r.width, r.height);
+ g2.setBackground(PDebug.getDebugPaintColor());
+ g2.fill(r2);
+ }
+
+ // create new paint context and set render quality
+ final PPaintContext paintContext = new PPaintContext(g2);
+ if (getInteracting() || getAnimating()) {
+ if (interactingRenderQuality > animatingRenderQuality) {
+ paintContext.setRenderQuality(interactingRenderQuality);
+ }
+ else {
+ paintContext.setRenderQuality(animatingRenderQuality);
+ }
+ }
+ else {
+ paintContext.setRenderQuality(defaultRenderQuality);
+ }
+
+ // paint Piccolo2D
+ camera.fullPaint(paintContext);
+
+ // if switched state from animating to not animating invalidate
+ // the entire screen so that it will be drawn with the default instead
+ // of animating render quality.
+ if (animatingOnLastPaint && !getAnimating()) {
+ repaint();
+ }
+ animatingOnLastPaint = getAnimating();
+
+ final boolean region = PDebug.debugRegionManagement;
+ PDebug.debugRegionManagement = false;
+ PDebug.endProcessingOutput(g2);
+ PDebug.debugRegionManagement = region;
+
+ if (doubleBuffered) {
+ gc.drawImage(backBuffer, 0, 0);
+
+ // Dispose of the allocated image gc
+ imageGC.dispose();
+ }
+ }
+
+ /**
+ * Performs an immediate repaint if no other client is currently performing
+ * one.
+ */
+ public void paintImmediately() {
+ if (paintingImmediately) {
+ return;
+ }
+
+ paintingImmediately = true;
+ redraw();
+ update();
+ paintingImmediately = false;
+ }
+
+ private final class KeyboardInputSource implements KeyListener {
+ public void keyPressed(final KeyEvent ke) {
+ final java.awt.event.KeyEvent inputEvent = new PSWTKeyEvent(ke, java.awt.event.KeyEvent.KEY_PRESSED);
+ sendInputEventToInputManager(inputEvent, java.awt.event.KeyEvent.KEY_PRESSED);
+ }
+
+ public void keyReleased(final KeyEvent ke) {
+ final java.awt.event.KeyEvent inputEvent = new PSWTKeyEvent(ke, java.awt.event.KeyEvent.KEY_RELEASED);
+ sendInputEventToInputManager(inputEvent, java.awt.event.KeyEvent.KEY_RELEASED);
+ }
+ }
+
+ private final class MouseInputSource implements MouseListener, MouseMoveListener {
+ public void mouseMove(final MouseEvent me) {
+ if (isButton1Pressed || isButton2Pressed || isButton3Pressed) {
+ final java.awt.event.MouseEvent inputEvent = new PSWTMouseEvent(me,
+ java.awt.event.MouseEvent.MOUSE_DRAGGED, 1);
+ sendInputEventToInputManager(inputEvent, java.awt.event.MouseEvent.MOUSE_DRAGGED);
+ }
+ else {
+ final java.awt.event.MouseEvent inputEvent = new PSWTMouseEvent(me,
+ java.awt.event.MouseEvent.MOUSE_MOVED, 1);
+ sendInputEventToInputManager(inputEvent, java.awt.event.MouseEvent.MOUSE_MOVED);
+ }
+ }
+
+ public void mouseDown(final MouseEvent mouseEvent) {
+ boolean shouldBalanceEvent = false;
+
+ switch (mouseEvent.button) {
+ case SWT_BUTTON1:
+ if (isButton1Pressed) {
+ shouldBalanceEvent = true;
+ }
+ isButton1Pressed = true;
+ break;
+ case SWT_BUTTON2:
+ if (isButton2Pressed) {
+ shouldBalanceEvent = true;
+ }
+ isButton2Pressed = true;
+ break;
+ case SWT_BUTTON3:
+ if (isButton3Pressed) {
+ shouldBalanceEvent = true;
+ }
+ isButton3Pressed = true;
+ break;
+ default:
+ }
+
+ if (shouldBalanceEvent) {
+ final java.awt.event.MouseEvent balanceEvent = new PSWTMouseEvent(mouseEvent,
+ java.awt.event.MouseEvent.MOUSE_RELEASED, 1);
+ sendInputEventToInputManager(balanceEvent, java.awt.event.MouseEvent.MOUSE_RELEASED);
+ }
+
+ final java.awt.event.MouseEvent balanceEvent = new PSWTMouseEvent(mouseEvent,
+ java.awt.event.MouseEvent.MOUSE_PRESSED, 1);
+ sendInputEventToInputManager(balanceEvent, java.awt.event.MouseEvent.MOUSE_PRESSED);
+ }
+
+ public void mouseUp(final MouseEvent me) {
+ boolean shouldBalanceEvent = false;
+
+ switch (me.button) {
+ case SWT_BUTTON1:
+ if (!isButton1Pressed) {
+ shouldBalanceEvent = true;
+ }
+ isButton1Pressed = false;
+ break;
+ case SWT_BUTTON2:
+ if (!isButton2Pressed) {
+ shouldBalanceEvent = true;
+ }
+ isButton2Pressed = false;
+ break;
+ case SWT_BUTTON3:
+ if (!isButton3Pressed) {
+ shouldBalanceEvent = true;
+ }
+ isButton3Pressed = false;
+ break;
+ default:
+ }
+
+ if (shouldBalanceEvent) {
+ final java.awt.event.MouseEvent balanceEvent = new PSWTMouseEvent(me,
+ java.awt.event.MouseEvent.MOUSE_PRESSED, 1);
+ sendInputEventToInputManager(balanceEvent, java.awt.event.MouseEvent.MOUSE_PRESSED);
+ }
+
+ final java.awt.event.MouseEvent balanceEvent = new PSWTMouseEvent(me,
+ java.awt.event.MouseEvent.MOUSE_RELEASED, 1);
+ sendInputEventToInputManager(balanceEvent, java.awt.event.MouseEvent.MOUSE_RELEASED);
+ }
+
+ public void mouseDoubleClick(final MouseEvent me) {
+ // This doesn't work with click event types for some reason - it
+ // has to do with how the click and release events are ordered,
+ // I think
+ java.awt.event.MouseEvent inputEvent = new PSWTMouseEvent(me, java.awt.event.MouseEvent.MOUSE_PRESSED, 2);
+ sendInputEventToInputManager(inputEvent, java.awt.event.MouseEvent.MOUSE_PRESSED);
+ inputEvent = new PSWTMouseEvent(me, java.awt.event.MouseEvent.MOUSE_RELEASED, 2);
+ sendInputEventToInputManager(inputEvent, java.awt.event.MouseEvent.MOUSE_RELEASED);
+ }
+ }
+}
diff --git a/swt/src/main/java/org/piccolo2d/extras/swt/PSWTHandle.java b/swt/src/main/java/org/piccolo2d/extras/swt/PSWTHandle.java
new file mode 100644
index 0000000..1d211d3
--- /dev/null
+++ b/swt/src/main/java/org/piccolo2d/extras/swt/PSWTHandle.java
@@ -0,0 +1,257 @@
+/*
+ * Copyright (c) 2008-2010, 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.extras.swt;
+
+import java.awt.Color;
+import java.awt.Shape;
+import java.awt.event.InputEvent;
+import java.awt.geom.Ellipse2D;
+import java.awt.geom.Point2D;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+
+import org.piccolo2d.PCamera;
+import org.piccolo2d.PNode;
+import org.piccolo2d.event.PDragSequenceEventHandler;
+import org.piccolo2d.event.PInputEvent;
+import org.piccolo2d.event.PInputEventFilter;
+import org.piccolo2d.extras.util.PLocator;
+import org.piccolo2d.extras.util.PNodeLocator;
+import org.piccolo2d.util.PBounds;
+import org.piccolo2d.util.PDimension;
+
+
+/**
+ * PSWTHandle is used to modify some aspect of Piccolo when it is dragged.
+ * Each handle has a PLocator that it uses to automatically position itself. See
+ * PSWTBoundsHandle for an example of a handle that resizes the bounds of another
+ * node.
+ * + * + * @version 1.0 + * @author Jesse Grosjean + */ +public class PSWTHandle extends PSWTPath { + private static final long serialVersionUID = 1L; + /** The Default Size of a handle not including its border. */ + public static final float DEFAULT_HANDLE_SIZE = 8; + /** The default shape to use when drawing handles. Default is an ellipse. */ + public static final Shape DEFAULT_HANDLE_SHAPE = new Ellipse2D.Float(0f, 0f, DEFAULT_HANDLE_SIZE, DEFAULT_HANDLE_SIZE); + /** The default color to use when drawing a handle. (white) */ + public static final Color DEFAULT_COLOR = Color.white; + + private PLocator locator; + private PDragSequenceEventHandler handleDragger; + + /** + * Construct a new handle that will use the given locator to locate itself + * on its parent node. + * + * @param aLocator locator to use when positioning this handle + */ + public PSWTHandle(final PLocator aLocator) { + super(DEFAULT_HANDLE_SHAPE); + locator = aLocator; + setPaint(DEFAULT_COLOR); + installHandleEventHandlers(); + } + + /** + * Installs the handler that will reposition the handle when it is dragged, + * and invoke appropriate call backs. + */ + protected void installHandleEventHandlers() { + handleDragger = new HandleDragHandler(); + + addPropertyChangeListener(PNode.PROPERTY_TRANSFORM, new PropertyChangeListener() { + public void propertyChange(final PropertyChangeEvent evt) { + relocateHandle(); + } + }); + + // so reject them so we don't consume them + addInputEventListener(handleDragger); + } + + /** + * Return the event handler that is responsible for the drag handle + * interaction. + * + * @return handler responsible for responding to drag events + */ + public PDragSequenceEventHandler getHandleDraggerHandler() { + return handleDragger; + } + + /** + * Get the locator that this handle uses to position itself on its parent + * node. + * + * @return locator used to position this handle + */ + public PLocator getLocator() { + return locator; + } + + /** + * Set the locator that this handle uses to position itself on its parent + * node. + * + * @param aLocator used to position this handle + */ + public void setLocator(final PLocator aLocator) { + locator = aLocator; + invalidatePaint(); + relocateHandle(); + } + + // **************************************************************** + // Handle Dragging - These are the methods the subclasses should + // normally override to give a handle unique behavior. + // **************************************************************** + + /** + * Override this method to get notified when the handle starts to get + * dragged. + * + * @param aLocalPoint point at which dragging was started relative to the + * handle's coordinate system + * @param aEvent event representing the start of the drag + */ + public void startHandleDrag(final Point2D aLocalPoint, final PInputEvent aEvent) { + } + + /** + * Override this method to get notified as the handle is dragged. + * + * @param aLocalDimension magnitude of the dragHandle event in the + * dimensions of the handle's coordinate system. + * @param aEvent event representing the drag + */ + public void dragHandle(final PDimension aLocalDimension, final PInputEvent aEvent) { + } + + /** + * Override this method to get notified when the handle stops getting + * dragged. + * + * @param aLocalPoint point at which dragging was ended relative to the + * handle's coordinate system + * @param aEvent event representing the end of the drag + */ + public void endHandleDrag(final Point2D aLocalPoint, final PInputEvent aEvent) { + } + + // **************************************************************** + // Layout - When a handle's parent's layout changes the handle + // invalidates its own layout and then repositions itself on its + // parents bounds using its locator to determine that new + // position. + // **************************************************************** + + /** {@inheritDoc} */ + public void setParent(final PNode newParent) { + super.setParent(newParent); + relocateHandle(); + } + + /** {@inheritDoc} */ + public void parentBoundsChanged() { + relocateHandle(); + } + + /** + * Force this handle to relocate itself using its locator. + */ + public void relocateHandle() { + if (locator != null) { + final PBounds b = getBoundsReference(); + final Point2D aPoint = locator.locatePoint(null); + + if (locator instanceof PNodeLocator) { + final PNode located = ((PNodeLocator) locator).getNode(); + final PNode parent = getParent(); + + located.localToGlobal(aPoint); + globalToLocal(aPoint); + + if (parent != located && parent instanceof PCamera) { + ((PCamera) parent).viewToLocal(aPoint); + } + } + + final double newCenterX = aPoint.getX(); + final double newCenterY = aPoint.getY(); + + if (newCenterX != b.getCenterX() || newCenterY != b.getCenterY()) { + centerBoundsOnPoint(newCenterX, newCenterY); + } + } + } + + // **************************************************************** + // Serialization + // **************************************************************** + + private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { + in.defaultReadObject(); + installHandleEventHandlers(); + } + + private final class HandleDragHandler extends PDragSequenceEventHandler { + public HandleDragHandler() { + final PInputEventFilter filter = new PInputEventFilter(InputEvent.BUTTON1_MASK); + setEventFilter(filter); + filter.setMarksAcceptedEventsAsHandled(true); + filter.setAcceptsMouseEntered(false); + filter.setAcceptsMouseExited(false); + filter.setAcceptsMouseMoved(false); + } + + protected void startDrag(final PInputEvent event) { + super.startDrag(event); + startHandleDrag(event.getPositionRelativeTo(PSWTHandle.this), event); + } + + protected void drag(final PInputEvent event) { + super.drag(event); + final PDimension aDelta = event.getDeltaRelativeTo(PSWTHandle.this); + if (aDelta.getWidth() != 0 || aDelta.getHeight() != 0) { + dragHandle(aDelta, event); + } + } + + protected void endDrag(final PInputEvent event) { + super.endDrag(event); + endHandleDrag(event.getPositionRelativeTo(PSWTHandle.this), event); + } + } +} \ No newline at end of file diff --git a/swt/src/main/java/org/piccolo2d/extras/swt/PSWTImage.java b/swt/src/main/java/org/piccolo2d/extras/swt/PSWTImage.java new file mode 100644 index 0000000..5bdbdd7 --- /dev/null +++ b/swt/src/main/java/org/piccolo2d/extras/swt/PSWTImage.java @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2008-2010, 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.extras.swt; + +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.Rectangle; +import org.piccolo2d.PNode; +import org.piccolo2d.nodes.PImage; +import org.piccolo2d.util.PBounds; +import org.piccolo2d.util.PPaintContext; + + +/** + * PSWTImage is a wrapper around a org.eclipse.swt.graphics.Image. + * + * @version 1.0 + * @author Jesse Grosjean + */ +public class PSWTImage extends PNode { + private static final long serialVersionUID = 1L; + + private final transient PSWTCanvas canvas; + + private transient Image image; + + /** + * Constructs a PSWTImage attached to the provided canvas and with a null + * image. + * + * The developer will need to call setImage for this node to be useful. + * + * TODO: determine if canvas is actually necessary + * + * @param canvas canvas to associate with the image node + */ + public PSWTImage(final PSWTCanvas canvas) { + this.canvas = canvas; + canvas.addDisposeListener(new DisposeListener() { + public void widgetDisposed(final DisposeEvent de) { + disposeImage(); + } + }); + } + + /** + * Constructs a PSWTImage wrapping the provided image. + * + * @param canvas canvas to associate with the image node + * @param image image to be wrapped by this PSWTImage + */ + public PSWTImage(final PSWTCanvas canvas, final Image image) { + this(canvas); + setImage(image); + } + + /** + * Constructs a PSWTImage wrapping the provided image after loading it from + * the file. + * + * @param canvas canvas to associate with the image node + * @param fileName path to the image, will be loaded and converted to an + * Image internally + */ + public PSWTImage(final PSWTCanvas canvas, final String fileName) { + this(canvas); + setImage(fileName); + } + + /** + * Returns the image that is shown by this node, may be null. + * + * @return the image that is shown by this node + */ + public Image getImage() { + return image; + } + + /** + * Set the image that is wrapped by this PImage node. This method will also + * load the image using a MediaTracker before returning. And if the this + * PImage is accelerated that image will be copied into an accelerated image + * if needed. Note that this may cause undesired results with images that + * have transparent regions, for those cases you may want to set the PImage + * to be not accelerated. + * + * @param filePath path to the file to load as an image + */ + public void setImage(final String filePath) { + setImage(new Image(canvas.getDisplay(), filePath)); + } + + /** + * Set the image that is wrapped by this PImage node. This method will also + * load the image using a MediaTracker before returning. And if the this + * PImage is accelerated that I'm will be copied into an accelerated image + * if needed. Note that this may cause undesired results with images that + * have transparent regions, for those cases you may want to set the PImage + * to be not accelerated. + * + * @param newImage the image that should replace the current one + */ + public void setImage(final Image newImage) { + final Image old = image; + image = newImage; + + if (image != null) { + final Rectangle bounds = getImage().getBounds(); + setBounds(0, 0, bounds.width, bounds.height); + invalidatePaint(); + } + + firePropertyChange(PImage.PROPERTY_CODE_IMAGE, PImage.PROPERTY_IMAGE, old, image); + } + + /** + * Subclasses may override this method to provide different image dispose + * behavior. + */ + protected void disposeImage() { + if (image != null) { + image.dispose(); + } + } + + /** {@inheritDoc} */ + protected void paint(final PPaintContext paintContext) { + if (getImage() != null) { + final Rectangle r = image.getBounds(); + final PBounds b = getBoundsReference(); + final SWTGraphics2D g2 = (SWTGraphics2D) paintContext.getGraphics(); + + if (b.x == 0 && b.y == 0 && b.width == r.width && b.height == r.height) { + g2.drawImage(image, 0, 0); + } + else { + g2.translate(b.x, b.y); + g2.scale(b.width / r.width, b.height / r.height); + g2.drawImage(image, 0, 0); + g2.scale(r.width / b.width, r.height / b.height); + g2.translate(-b.x, -b.y); + } + } + } +} diff --git a/swt/src/main/java/org/piccolo2d/extras/swt/PSWTKeyEvent.java b/swt/src/main/java/org/piccolo2d/extras/swt/PSWTKeyEvent.java new file mode 100644 index 0000000..280aa92 --- /dev/null +++ b/swt/src/main/java/org/piccolo2d/extras/swt/PSWTKeyEvent.java @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2008-2010, 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.extras.swt; + +import java.awt.Component; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Widget; + +/** + * Key event overridden to wrap an SWT KeyEvent as a swing KeyEvent. + * + * @author Lance Good + */ +public class PSWTKeyEvent extends KeyEvent { + private static final long serialVersionUID = 1L; + + private static Component fakeSrc = new Component() { + }; + + private org.eclipse.swt.events.KeyEvent swtEvent; + + /** + * Creates an object that wraps a SWT Key event. Making it queriable from + * Piccolo2d as though it were a Swing one. + * + * @param ke key event object + * @param eventType type of key event + */ + public PSWTKeyEvent(final org.eclipse.swt.events.KeyEvent ke, final int eventType) { + super(fakeSrc, eventType, ke.time, 0, ke.keyCode, ke.character, KeyEvent.KEY_LOCATION_STANDARD); + + swtEvent = ke; + } + + /** {@inheritDoc} */ + public Object getSource() { + return swtEvent.getSource(); + } + + /** {@inheritDoc} */ + public boolean isShiftDown() { + return (swtEvent.stateMask & SWT.SHIFT) != 0; + } + + /** {@inheritDoc} */ + public boolean isControlDown() { + return (swtEvent.stateMask & SWT.CONTROL) != 0; + } + + /** {@inheritDoc} */ + public boolean isAltDown() { + return (swtEvent.stateMask & SWT.ALT) != 0; + } + + /** {@inheritDoc} */ + public int getModifiers() { + int modifiers = 0; + + if (swtEvent != null) { + if ((swtEvent.stateMask & SWT.ALT) != 0) { + modifiers = modifiers | InputEvent.ALT_MASK; + } + if ((swtEvent.stateMask & SWT.CONTROL) != 0) { + modifiers = modifiers | InputEvent.CTRL_MASK; + } + if ((swtEvent.stateMask & SWT.SHIFT) != 0) { + modifiers = modifiers | InputEvent.SHIFT_MASK; + } + } + + return modifiers; + } + + /** {@inheritDoc} */ + public int getModifiersEx() { + int modifiers = 0; + + if (swtEvent != null) { + if ((swtEvent.stateMask & SWT.ALT) != 0) { + modifiers = modifiers | InputEvent.ALT_DOWN_MASK; + } + if ((swtEvent.stateMask & SWT.CONTROL) != 0) { + modifiers = modifiers | InputEvent.CTRL_DOWN_MASK; + } + if ((swtEvent.stateMask & SWT.SHIFT) != 0) { + modifiers = modifiers | InputEvent.SHIFT_DOWN_MASK; + } + } + + return modifiers; + } + + /** {@inheritDoc} */ + public boolean isActionKey() { + return false; + } + + /** + * Returns the widget from which the event was emitted. + * + * @return source widget + */ + public Widget getWidget() { + return swtEvent.widget; + } + + /** + * Return the display on which the interaction occurred. + * + * @return display on which the interaction occurred + */ + public Display getDisplay() { + return swtEvent.display; + } + + /** + * Return the associated SWT data for the event. + * + * @return data associated to the SWT event + */ + public Object getData() { + return swtEvent.data; + } +} diff --git a/swt/src/main/java/org/piccolo2d/extras/swt/PSWTMouseEvent.java b/swt/src/main/java/org/piccolo2d/extras/swt/PSWTMouseEvent.java new file mode 100644 index 0000000..18ebc54 --- /dev/null +++ b/swt/src/main/java/org/piccolo2d/extras/swt/PSWTMouseEvent.java @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2008-2010, 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.extras.swt; + +import java.awt.Component; +import java.awt.event.InputEvent; +import java.awt.event.MouseEvent; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Widget; + +/** + * Mouse event overridden to wrap an SWT MouseEvent as a Swing MouseEvent. + * + * @author Lance Good + */ +public class PSWTMouseEvent extends MouseEvent { + private static final int SWT_BUTTON1 = 1; + private static final int SWT_BUTTON2 = 2; + private static final int SWT_BUTTON3 = 3; + + private static final long serialVersionUID = 1L; + + private static Component fakeSrc = new Component() { + }; + + /** Event being wrapped. */ + protected org.eclipse.swt.events.MouseEvent swtEvent; + + /** Number times the mouse was clicked in relation to the wrapped event. */ + protected int clickCount; + + /** + * Constructs a PSWTMouseEvent that wraps the provided SWT MouseEvent as a + * Swing one. + * + * @param me Mouse Event being wrapped + * @param type event type + * @param clickCount number of times the mouse has been clicked + */ + public PSWTMouseEvent(final org.eclipse.swt.events.MouseEvent me, final int type, final int clickCount) { + super(fakeSrc, type, me.time, 0, me.x, me.y, clickCount, me.button == SWT_BUTTON3, me.button); + + swtEvent = me; + this.clickCount = clickCount; + } + + /** {@inheritDoc} */ + public Object getSource() { + return swtEvent.getSource(); + } + + /** {@inheritDoc} */ + public int getClickCount() { + return clickCount; + } + + /** {@inheritDoc} */ + public int getButton() { + switch (swtEvent.button) { + case SWT_BUTTON1: + return MouseEvent.BUTTON1; + case SWT_BUTTON2: + return MouseEvent.BUTTON2; + case SWT_BUTTON3: + return MouseEvent.BUTTON3; + default: + return MouseEvent.NOBUTTON; + } + } + + /** {@inheritDoc} */ + public boolean isShiftDown() { + return (swtEvent.stateMask & SWT.SHIFT) != 0; + } + + /** {@inheritDoc} */ + public boolean isControlDown() { + return (swtEvent.stateMask & SWT.CONTROL) != 0; + } + + /** {@inheritDoc} */ + public boolean isAltDown() { + return (swtEvent.stateMask & SWT.ALT) != 0; + } + + /** {@inheritDoc} */ + public int getModifiers() { + int modifiers = 0; + + if (swtEvent != null) { + if ((swtEvent.stateMask & SWT.ALT) != 0) { + modifiers = modifiers | InputEvent.ALT_MASK; + } + if ((swtEvent.stateMask & SWT.CONTROL) != 0) { + modifiers = modifiers | InputEvent.CTRL_MASK; + } + if ((swtEvent.stateMask & SWT.SHIFT) != 0) { + modifiers = modifiers | InputEvent.SHIFT_MASK; + } + if (swtEvent.button == SWT_BUTTON1 || (swtEvent.stateMask & SWT.BUTTON1) != 0) { + modifiers = modifiers | InputEvent.BUTTON1_MASK; + } + if (swtEvent.button == SWT_BUTTON2 || (swtEvent.stateMask & SWT.BUTTON2) != 0) { + modifiers = modifiers | InputEvent.BUTTON2_MASK; + } + if (swtEvent.button == SWT_BUTTON3 || (swtEvent.stateMask & SWT.BUTTON3) != 0) { + modifiers = modifiers | InputEvent.BUTTON3_MASK; + } + } + + return modifiers; + } + + /** {@inheritDoc} */ + public int getModifiersEx() { + int modifiers = 0; + + if (swtEvent != null) { + if ((swtEvent.stateMask & SWT.ALT) != 0) { + modifiers = modifiers | InputEvent.ALT_DOWN_MASK; + } + if ((swtEvent.stateMask & SWT.CONTROL) != 0) { + modifiers = modifiers | InputEvent.CTRL_DOWN_MASK; + } + if ((swtEvent.stateMask & SWT.SHIFT) != 0) { + modifiers = modifiers | InputEvent.SHIFT_DOWN_MASK; + } + if (swtEvent.button == SWT_BUTTON1 || (swtEvent.stateMask & SWT.BUTTON1) != 0) { + modifiers = modifiers | InputEvent.BUTTON1_DOWN_MASK; + } + if (swtEvent.button == SWT_BUTTON2 || (swtEvent.stateMask & SWT.BUTTON2) != 0) { + modifiers = modifiers | InputEvent.BUTTON2_DOWN_MASK; + } + if (swtEvent.button == SWT_BUTTON3 || (swtEvent.stateMask & SWT.BUTTON3) != 0) { + modifiers = modifiers | InputEvent.BUTTON3_DOWN_MASK; + } + } + + return modifiers; + } + + /** + * Returns the widget from which the event was emitted. + * + * @return source widget + */ + public Widget getWidget() { + return swtEvent.widget; + } + + /** + * Return the display on which the interaction occurred. + * + * @return display on which the interaction occurred + */ + public Display getDisplay() { + return swtEvent.display; + } + + /** + * Return the associated SWT data for the event. + * + * @return data associated to the SWT event + */ + public Object getData() { + return swtEvent.data; + } +} diff --git a/swt/src/main/java/org/piccolo2d/extras/swt/PSWTPath.java b/swt/src/main/java/org/piccolo2d/extras/swt/PSWTPath.java new file mode 100644 index 0000000..bfb48d8 --- /dev/null +++ b/swt/src/main/java/org/piccolo2d/extras/swt/PSWTPath.java @@ -0,0 +1,574 @@ +/* + * Copyright (c) 2008-2010, 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.extras.swt; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Paint; +import java.awt.Shape; +import java.awt.geom.AffineTransform; +import java.awt.geom.Arc2D; +import java.awt.geom.Ellipse2D; +import java.awt.geom.GeneralPath; +import java.awt.geom.Line2D; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.awt.geom.RoundRectangle2D; + +import org.piccolo2d.PNode; +import org.piccolo2d.nodes.PPath; +import org.piccolo2d.util.PAffineTransform; +import org.piccolo2d.util.PAffineTransformException; +import org.piccolo2d.util.PBounds; +import org.piccolo2d.util.PPaintContext; + + +/** + * PSWTPath is a wrapper around a java.awt.geom.GeneralPath, with + * workarounds for drawing shapes in SWT where necessary. + * + * @version 1.0 + * @author Jesse Grosjean + */ +public class PSWTPath extends PNode { + private static final long serialVersionUID = 1L; + + /** + * The property name that identifies a change of this node's path. In any + * property change event the new value will be a reference to this node's + * path, but old value will always be null. + */ + public static final String PROPERTY_SHAPE = "shape"; + + private static final double BOUNDS_TOLERANCE = 0.01; + private static final Rectangle2D.Float TEMP_RECTANGLE = new Rectangle2D.Float(); + private static final RoundRectangle2D.Float TEMP_ROUNDRECTANGLE = new RoundRectangle2D.Float(); + private static final Ellipse2D.Float TEMP_ELLIPSE = new Ellipse2D.Float(); + private static final Color DEFAULT_STROKE_PAINT = Color.black; + private static final BasicStroke BASIC_STROKE = new BasicStroke(); + private static final float PEN_WIDTH = 1f; + + private Paint strokePaint; + + private boolean updatingBoundsFromPath; + private Shape origShape; + private Shape shape; + + private PAffineTransform internalXForm; + private AffineTransform inverseXForm; + + private double[] shapePts; + + /** + * Creates a path representing the rectangle provided. + * + * @param x left of rectangle + * @param y top of rectangle + * @param width width of rectangle + * @param height height of rectangle + * @return created rectangle + */ + public static PSWTPath createRectangle(final float x, final float y, final float width, final float height) { + TEMP_RECTANGLE.setFrame(x, y, width, height); + final PSWTPath result = new PSWTPath(TEMP_RECTANGLE); + result.setPaint(Color.white); + return result; + } + + /** + * Creates a path representing the rounded rectangle provided. + * + * @param x left of rectangle + * @param y top of rectangle + * @param width width of rectangle + * @param height height of rectangle + * @param arcWidth width of the arc at the corners + * @param arcHeight height of arc at the corners + * @return created rounded rectangle + */ + public static PSWTPath createRoundRectangle(final float x, final float y, final float width, final float height, + final float arcWidth, final float arcHeight) { + TEMP_ROUNDRECTANGLE.setRoundRect(x, y, width, height, arcWidth, arcHeight); + final PSWTPath result = new PSWTPath(TEMP_ROUNDRECTANGLE); + result.setPaint(Color.white); + return result; + } + + /** + * Creates a path representing an ellipse that covers the rectangle + * provided. + * + * @param x left of rectangle + * @param y top of rectangle + * @param width width of rectangle + * @param height height of rectangle + * @return created ellipse + */ + public static PSWTPath createEllipse(final float x, final float y, final float width, final float height) { + TEMP_ELLIPSE.setFrame(x, y, width, height); + final PSWTPath result = new PSWTPath(TEMP_ELLIPSE); + result.setPaint(Color.white); + return result; + } + + /** + * Creates a PPath for the poly-line for the given points. + * + * @param points array of points for the point lines + * + * @return created poly-line for the given points + */ + public static PSWTPath createPolyline(final Point2D[] points) { + final PSWTPath result = new PSWTPath(); + result.setPathToPolyline(points); + result.setPaint(Color.white); + return result; + } + + /** + * Creates a PPath for the poly-line for the given points. + * + * @param xp array of x components of the points of the poly-lines + * @param yp array of y components of the points of the poly-lines + * + * @return created poly-line for the given points + */ + public static PSWTPath createPolyline(final float[] xp, final float[] yp) { + final PSWTPath result = new PSWTPath(); + result.setPathToPolyline(xp, yp); + result.setPaint(Color.white); + return result; + } + + /** + * Creates an empty PSWTPath. + */ + public PSWTPath() { + strokePaint = DEFAULT_STROKE_PAINT; + } + + /** + * Creates an SWTPath in the given shape with the default paint and stroke. + * + * @param aShape the desired shape + */ + public PSWTPath(final Shape aShape) { + this(); + setShape(aShape); + } + + // **************************************************************** + // Stroke + // **************************************************************** + /** + * Returns the paint to use when drawing the stroke of the shape. + * + * @return path's stroke paint + */ + public Paint getStrokePaint() { + return strokePaint; + } + + /** + * Sets the paint to use when drawing the stroke of the shape. + * + * @param strokeColor new stroke color + */ + public void setStrokeColor(final Paint strokeColor) { + final Paint old = strokePaint; + strokePaint = strokeColor; + invalidatePaint(); + firePropertyChange(PPath.PROPERTY_CODE_STROKE_PAINT, PPath.PROPERTY_STROKE_PAINT, old, strokePaint); + } + + /** + * Set the bounds of this path. This method works by scaling the path to fit + * into the specified bounds. This normally works well, but if the specified + * base bounds get too small then it is impossible to expand the path shape + * again since all its numbers have tended to zero, so application code may + * need to take this into consideration. + * + * @param x new left position of bounds + * @param y new top position of bounds + * @param width the new width of the bounds + * @param height the new height of the bounds + */ + protected void internalUpdateBounds(final double x, final double y, final double width, final double height) { + if (updatingBoundsFromPath) { + return; + } + if (origShape == null) { + return; + } + + final Rectangle2D pathBounds = origShape.getBounds2D(); + + if (Math.abs(x - pathBounds.getX()) / x < BOUNDS_TOLERANCE + && Math.abs(y - pathBounds.getY()) / y < BOUNDS_TOLERANCE + && Math.abs(width - pathBounds.getWidth()) / width < BOUNDS_TOLERANCE + && Math.abs(height - pathBounds.getHeight()) / height < BOUNDS_TOLERANCE) { + return; + } + + if (internalXForm == null) { + internalXForm = new PAffineTransform(); + } + internalXForm.setToIdentity(); + internalXForm.translate(x, y); + internalXForm.scale(width / pathBounds.getWidth(), height / pathBounds.getHeight()); + internalXForm.translate(-pathBounds.getX(), -pathBounds.getY()); + + try { + inverseXForm = internalXForm.createInverse(); + } + catch (final Exception e) { + throw new PAffineTransformException("unable to invert transform", internalXForm); + } + } + + /** + * Returns true if path crosses the provided bounds. Takes visibility of + * path into account. + * + * @param aBounds bounds being tested for intersection + * @return true if path visibly crosses bounds + */ + public boolean intersects(final Rectangle2D aBounds) { + if (super.intersects(aBounds)) { + final Rectangle2D srcBounds; + if (internalXForm == null) { + srcBounds = aBounds; + } + else { + srcBounds = new PBounds(aBounds); + internalXForm.inverseTransform(srcBounds, srcBounds); + } + + if (getPaint() != null && shape.intersects(srcBounds)) { + return true; + } + else if (strokePaint != null) { + return BASIC_STROKE.createStrokedShape(shape).intersects(srcBounds); + } + } + return false; + } + + /** + * Recalculates the path's bounds by examining it's associated shape. + */ + public void updateBoundsFromPath() { + updatingBoundsFromPath = true; + + if (origShape == null) { + resetBounds(); + } + else { + final Rectangle2D b = origShape.getBounds2D(); + + // Note that this pen width code does not really work for SWT since + // it assumes + // that the pen width scales - in actuality it does not. However, + // the fix would + // be to have volatile bounds for all shapes which isn't a nice + // alternative + super.setBounds(b.getX() - PEN_WIDTH, b.getY() - PEN_WIDTH, b.getWidth() + 2 * PEN_WIDTH, b.getHeight() + 2 + * PEN_WIDTH); + } + updatingBoundsFromPath = false; + } + + // **************************************************************** + // Painting + // **************************************************************** + /** + * Paints the path on the context provided. + * + * @param paintContext the context onto which the path will be painted + */ + protected void paint(final PPaintContext paintContext) { + final Paint p = getPaint(); + final SWTGraphics2D g2 = (SWTGraphics2D) paintContext.getGraphics(); + + if (internalXForm != null) { + g2.transform(internalXForm); + } + + if (p != null) { + g2.setBackground((Color) p); + fillShape(g2); + } + + if (strokePaint != null) { + g2.setColor((Color) strokePaint); + drawShape(g2); + } + + if (inverseXForm != null) { + g2.transform(inverseXForm); + } + } + + private void drawShape(final SWTGraphics2D g2) { + final double lineWidth = g2.getTransformedLineWidth(); + if (shape instanceof Rectangle2D) { + g2.drawRect(shapePts[0] + lineWidth / 2, shapePts[1] + lineWidth / 2, shapePts[2] - lineWidth, shapePts[3] + - lineWidth); + } + else if (shape instanceof Ellipse2D) { + g2.drawOval(shapePts[0] + lineWidth / 2, shapePts[1] + lineWidth / 2, shapePts[2] - lineWidth, shapePts[3] + - lineWidth); + } + else if (shape instanceof Arc2D) { + g2.drawArc(shapePts[0] + lineWidth / 2, shapePts[1] + lineWidth / 2, shapePts[2] - lineWidth, shapePts[3] + - lineWidth, shapePts[4], shapePts[5]); + } + else if (shape instanceof RoundRectangle2D) { + g2.drawRoundRect(shapePts[0] + lineWidth / 2, shapePts[1] + lineWidth / 2, shapePts[2] - lineWidth, + shapePts[3] - lineWidth, shapePts[4], shapePts[5]); + } + else { + g2.draw(shape); + } + } + + private void fillShape(final SWTGraphics2D g2) { + final double lineWidth = g2.getTransformedLineWidth(); + if (shape instanceof Rectangle2D) { + g2.fillRect(shapePts[0] + lineWidth / 2, shapePts[1] + lineWidth / 2, shapePts[2] - lineWidth, shapePts[3] + - lineWidth); + } + else if (shape instanceof Ellipse2D) { + g2.fillOval(shapePts[0] + lineWidth / 2, shapePts[1] + lineWidth / 2, shapePts[2] - lineWidth, shapePts[3] + - lineWidth); + } + else if (shape instanceof Arc2D) { + g2.fillArc(shapePts[0] + lineWidth / 2, shapePts[1] + lineWidth / 2, shapePts[2] - lineWidth, shapePts[3] + - lineWidth, shapePts[4], shapePts[5]); + } + else if (shape instanceof RoundRectangle2D) { + g2.fillRoundRect(shapePts[0] + lineWidth / 2, shapePts[1] + lineWidth / 2, shapePts[2] - lineWidth, + shapePts[3] - lineWidth, shapePts[4], shapePts[5]); + } + else { + g2.fill(shape); + } + } + + /** + * Changes the underlying shape of this PSWTPath. + * + * @param newShape new associated shape of this PSWTPath + */ + public void setShape(final Shape newShape) { + shape = cloneShape(newShape); + origShape = shape; + updateShapePoints(newShape); + + firePropertyChange(PPath.PROPERTY_CODE_PATH, PPath.PROPERTY_PATH, null, shape); + updateBoundsFromPath(); + invalidatePaint(); + } + + /** + * Updates the internal points used to draw the shape. + * + * @param aShape shape to read points from + */ + public void updateShapePoints(final Shape aShape) { + if (aShape instanceof Rectangle2D) { + if (shapePts == null || shapePts.length < 4) { + shapePts = new double[4]; + } + + shapePts[0] = ((Rectangle2D) shape).getX(); + shapePts[1] = ((Rectangle2D) shape).getY(); + shapePts[2] = ((Rectangle2D) shape).getWidth(); + shapePts[3] = ((Rectangle2D) shape).getHeight(); + } + else if (aShape instanceof Ellipse2D) { + if (shapePts == null || shapePts.length < 4) { + shapePts = new double[4]; + } + + shapePts[0] = ((Ellipse2D) shape).getX(); + shapePts[1] = ((Ellipse2D) shape).getY(); + shapePts[2] = ((Ellipse2D) shape).getWidth(); + shapePts[3] = ((Ellipse2D) shape).getHeight(); + } + else if (aShape instanceof Arc2D) { + if (shapePts == null || shapePts.length < 6) { + shapePts = new double[6]; + } + + shapePts[0] = ((Arc2D) shape).getX(); + shapePts[1] = ((Arc2D) shape).getY(); + shapePts[2] = ((Arc2D) shape).getWidth(); + shapePts[3] = ((Arc2D) shape).getHeight(); + shapePts[4] = ((Arc2D) shape).getAngleStart(); + shapePts[5] = ((Arc2D) shape).getAngleExtent(); + } + else if (aShape instanceof RoundRectangle2D) { + if (shapePts == null || shapePts.length < 6) { + shapePts = new double[6]; + } + + shapePts[0] = ((RoundRectangle2D) shape).getX(); + shapePts[1] = ((RoundRectangle2D) shape).getY(); + shapePts[2] = ((RoundRectangle2D) shape).getWidth(); + shapePts[3] = ((RoundRectangle2D) shape).getHeight(); + shapePts[4] = ((RoundRectangle2D) shape).getArcWidth(); + shapePts[5] = ((RoundRectangle2D) shape).getArcHeight(); + } + else { + shapePts = SWTShapeManager.shapeToPolyline(shape); + } + } + + /** + * Clone's the shape provided. + * + * @param aShape shape to be cloned + * + * @return a cloned version of the provided shape + */ + public Shape cloneShape(final Shape aShape) { + if (aShape instanceof Rectangle2D) { + return new PBounds((Rectangle2D) aShape); + } + else if (aShape instanceof Ellipse2D) { + final Ellipse2D e2 = (Ellipse2D) aShape; + return new Ellipse2D.Double(e2.getX(), e2.getY(), e2.getWidth(), e2.getHeight()); + } + else if (aShape instanceof Arc2D) { + final Arc2D a2 = (Arc2D) aShape; + return new Arc2D.Double(a2.getX(), a2.getY(), a2.getWidth(), a2.getHeight(), a2.getAngleStart(), a2 + .getAngleExtent(), a2.getArcType()); + } + else if (aShape instanceof RoundRectangle2D) { + final RoundRectangle2D r2 = (RoundRectangle2D) aShape; + return new RoundRectangle2D.Double(r2.getX(), r2.getY(), r2.getWidth(), r2.getHeight(), r2.getArcWidth(), + r2.getArcHeight()); + } + else if (aShape instanceof Line2D) { + final Line2D l2 = (Line2D) aShape; + return new Line2D.Double(l2.getP1(), l2.getP2()); + } + else { + final GeneralPath aPath = new GeneralPath(); + aPath.append(aShape, false); + return aPath; + } + } + + /** + * Resets the path to a rectangle with the dimensions and position provided. + * + * @param x left of the rectangle + * @param y top of te rectangle + * @param width width of the rectangle + * @param height height of the rectangle + */ + public void setPathToRectangle(final float x, final float y, final float width, final float height) { + TEMP_RECTANGLE.setFrame(x, y, width, height); + setShape(TEMP_RECTANGLE); + } + + /** + * Resets the path to a rectangle with the dimensions and position provided. + * + * @param x left of the rectangle + * @param y top of te rectangle + * @param width width of the rectangle + * @param height height of the rectangle + * @param arcWidth width of arc in the corners of the rectangle + * @param arcHeight height of arc in the corners of the rectangle + */ + public void setPathToRoundRectangle(final float x, final float y, final float width, final float height, + final float arcWidth, final float arcHeight) { + TEMP_ROUNDRECTANGLE.setRoundRect(x, y, width, height, arcWidth, arcHeight); + setShape(TEMP_ROUNDRECTANGLE); + } + + /** + * Resets the path to an ellipse positioned at the coordinate provided with + * the dimensions provided. + * + * @param x left of the ellipse + * @param y top of the ellipse + * @param width width of the ellipse + * @param height height of the ellipse + */ + public void setPathToEllipse(final float x, final float y, final float width, final float height) { + TEMP_ELLIPSE.setFrame(x, y, width, height); + setShape(TEMP_ELLIPSE); + } + + /** + * Sets the path to a sequence of segments described by the points. + * + * @param points points to that lie along the generated path + */ + public void setPathToPolyline(final Point2D[] points) { + final GeneralPath path = new GeneralPath(); + path.reset(); + path.moveTo((float) points[0].getX(), (float) points[0].getY()); + for (int i = 1; i < points.length; i++) { + path.lineTo((float) points[i].getX(), (float) points[i].getY()); + } + setShape(path); + } + + /** + * Sets the path to a sequence of segments described by the point components + * provided. + * + * @param xp the x components of the points along the path + * @param yp the y components of the points along the path + */ + public void setPathToPolyline(final float[] xp, final float[] yp) { + final GeneralPath path = new GeneralPath(); + path.reset(); + path.moveTo(xp[0], yp[0]); + for (int i = 1; i < xp.length; i++) { + path.lineTo(xp[i], yp[i]); + } + setShape(path); + } + + /** + * Return the center of this SWT path node, based on its bounds. + * + * @return the center of this SWT path node, based on its bounds + */ + public Point2D getCenter() { + PBounds bounds = getBoundsReference(); + return new Point2D.Double(bounds.x + (bounds.width / 2.0), bounds.y + (bounds.height / 2.0)); + } +} \ No newline at end of file diff --git a/swt/src/main/java/org/piccolo2d/extras/swt/PSWTRoot.java b/swt/src/main/java/org/piccolo2d/extras/swt/PSWTRoot.java new file mode 100644 index 0000000..ba7ba68 --- /dev/null +++ b/swt/src/main/java/org/piccolo2d/extras/swt/PSWTRoot.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2008-2010, 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.extras.swt; + +import java.awt.event.ActionListener; + +import javax.swing.Timer; + +import org.eclipse.swt.widgets.Composite; +import org.piccolo2d.PRoot; + + +/** + * PSWTRoot is a subclass of PRoot that is designed to work in the SWT + * environment. In particular it uses SWTTimers and the SWT event dispatch + * thread. With the current setup only a single PSWTCanvas is expected to be + * connected to a root. + * + * @version 1.1 + * @author Jesse Grosjean + */ +public class PSWTRoot extends PRoot { + private static final long serialVersionUID = 1L; + private final Composite composite; + + /** + * Constructs a PSWTRoot attached to the provided composite. + * + * @param composite composite PSWTRoot is responsible for + */ + public PSWTRoot(final Composite composite) { + this.composite = composite; + } + + /** + * Creates a timer that will fire the listener every delay milliseconds. + * + * @param delay time in milliseconds between firings of listener + * @param listener listener to be fired + * + * @return the created timer + */ + public Timer createTimer(final int delay, final ActionListener listener) { + return new SWTTimer(composite.getDisplay(), delay, listener); + } + + /** + * Processes Inputs if any kind of IO needs to be done. + */ + public void scheduleProcessInputsIfNeeded() { + if (!Thread.currentThread().equals(composite.getDisplay().getThread())) { + return; + } + + if (!processInputsScheduled && !processingInputs + && (getFullBoundsInvalid() || getChildBoundsInvalid() || getPaintInvalid() || getChildPaintInvalid())) { + + processInputsScheduled = true; + composite.getDisplay().asyncExec(new Runnable() { + public void run() { + processInputs(); + processInputsScheduled = false; + } + }); + } + } +} diff --git a/swt/src/main/java/org/piccolo2d/extras/swt/PSWTSelectionEventHandler.java b/swt/src/main/java/org/piccolo2d/extras/swt/PSWTSelectionEventHandler.java new file mode 100644 index 0000000..c8324e3 --- /dev/null +++ b/swt/src/main/java/org/piccolo2d/extras/swt/PSWTSelectionEventHandler.java @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2008-2010, 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.extras.swt; + +import java.awt.Color; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.util.List; + +import org.eclipse.swt.SWT; +import org.piccolo2d.PCamera; +import org.piccolo2d.PNode; +import org.piccolo2d.event.PInputEvent; +import org.piccolo2d.extras.event.PSelectionEventHandler; +import org.piccolo2d.util.PBounds; +import org.piccolo2d.util.PPaintContext; + + +/** + * Selection event handler modified to use SWT paths instead of normal paths. + * + * @version 1.0 + * @author Lance Good + */ +public class PSWTSelectionEventHandler extends PSelectionEventHandler { + + PSWTPath marquee; + PNode marqueeParent; + Point2D pressPt; + Point2D canvasPressPt; + + /** + * Creates a selection event handler. + * + * @param marqueeParent The node to which the event handler dynamically adds + * a marquee (temporarily) to represent the area being selected. + * @param selectableParent The node whose children will be selected by this + * event handler. + */ + public PSWTSelectionEventHandler(final PNode marqueeParent, final PNode selectableParent) { + super(new PNode(), selectableParent); + this.marqueeParent = marqueeParent; + } + + /** + * Creates a selection event handler. + * + * @param marqueeParent The node to which the event handler dynamically adds + * a marquee (temporarily) to represent the area being selected. + * @param selectableParents A list of nodes whose children will be selected + * by this event handler. + */ + public PSWTSelectionEventHandler(final PNode marqueeParent, final List selectableParents) { + super(new PNode(), selectableParents); + this.marqueeParent = marqueeParent; + } + + /** + * Modifies the provided node so that it is displayed as selected. + * + * @param node node to be decorated + */ + public void decorateSelectedNode(final PNode node) { + PSWTBoundsHandle.addBoundsHandlesTo(node); + } + + /** + * Undoes any modifications to the provided node so that it is not displayed as selected. + * + * @param node node to be undecorated + */ + public void undecorateSelectedNode(final PNode node) { + PSWTBoundsHandle.removeBoundsHandlesFrom(node); + } + + /** {@inheritDoc} */ + protected void initializeSelection(final PInputEvent pie) { + super.initializeSelection(pie); + pressPt = pie.getPosition(); + canvasPressPt = pie.getCanvasPosition(); + } + + /** {@inheritDoc} */ + protected void initializeMarquee(final PInputEvent e) { + super.initializeMarquee(e); + + marquee = new PSWTPath(new Rectangle2D.Float((float) pressPt.getX(), (float) pressPt.getY(), 0, 0)) { + /** + * + */ + private static final long serialVersionUID = 1L; + + protected void paint(final PPaintContext paintContext) { + final SWTGraphics2D s2g = (SWTGraphics2D) paintContext.getGraphics(); + s2g.gc.setLineStyle(SWT.LINE_DASH); + super.paint(paintContext); + s2g.gc.setLineStyle(SWT.LINE_SOLID); + } + }; + marquee.setStrokeColor(Color.black); + marquee.setPaint(null); + marqueeParent.addChild(marquee); + } + + /** {@inheritDoc} */ + protected void updateMarquee(final PInputEvent pie) { + super.updateMarquee(pie); + + final PBounds b = new PBounds(); + + if (marqueeParent instanceof PCamera) { + b.add(canvasPressPt); + b.add(pie.getCanvasPosition()); + } + else { + b.add(pressPt); + b.add(pie.getPosition()); + } + + marquee.setPathToRectangle((float) b.x, (float) b.y, (float) b.width, (float) b.height); + b.reset(); + b.add(pressPt); + b.add(pie.getPosition()); + } + + /** {@inheritDoc} */ + protected PBounds getMarqueeBounds() { + if (marquee != null) { + return marquee.getBounds(); + } + return new PBounds(); + } + + /** {@inheritDoc} */ + protected void endMarqueeSelection(final PInputEvent e) { + super.endMarqueeSelection(e); + + marquee.removeFromParent(); + marquee = null; + } + + /** {@inheritDoc} */ + protected void dragActivityStep(final PInputEvent aEvent) { + } +} \ No newline at end of file diff --git a/swt/src/main/java/org/piccolo2d/extras/swt/PSWTStickyHandleManager.java b/swt/src/main/java/org/piccolo2d/extras/swt/PSWTStickyHandleManager.java new file mode 100644 index 0000000..c44f22d --- /dev/null +++ b/swt/src/main/java/org/piccolo2d/extras/swt/PSWTStickyHandleManager.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2008-2010, 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.extras.swt; + +import org.piccolo2d.PCamera; +import org.piccolo2d.PNode; +import org.piccolo2d.util.PBounds; +import org.piccolo2d.util.PPickPath; + +/** + * A class for managing the position of a sticky handle. + */ +public class PSWTStickyHandleManager extends PNode { + private static final long serialVersionUID = 1L; + private PNode target; + private PCamera camera; + + /** + * Creates a sticky handle that will be displayed on the given camera and + * will update the provided target. + * + * @param camera camera on which to display the sticky handle + * @param target target being controlled by the handle + */ + public PSWTStickyHandleManager(final PCamera camera, final PNode target) { + setCameraTarget(camera, target); + PSWTBoundsHandle.addBoundsHandlesTo(this); + } + + /** + * Changes the associated camera and target for this sticky handle. + * + * @param newCamera new camera onto which this handle should appear + * @param newTarget new target which this handle will control + */ + public void setCameraTarget(final PCamera newCamera, final PNode newTarget) { + camera = newCamera; + camera.addChild(this); + target = newTarget; + } + + /** {@inheritDoc} */ + public boolean setBounds(final double x, final double y, final double width, final double height) { + final PBounds b = new PBounds(x, y, width, height); + camera.localToGlobal(b); + camera.localToView(b); + target.globalToLocal(b); + target.setBounds(b); + return super.setBounds(x, y, width, height); + } + + /** + * Always returns true to ensure that they will always be displayed + * appropriately. + * + * @return true + */ + protected boolean getBoundsVolatile() { + return true; + } + + /** {@inheritDoc} */ + public PBounds getBoundsReference() { + final PBounds targetBounds = target.getFullBounds(); + camera.viewToLocal(targetBounds); + camera.globalToLocal(targetBounds); + final PBounds bounds = super.getBoundsReference(); + bounds.setRect(targetBounds); + return super.getBoundsReference(); + } + + /** {@inheritDoc} */ + public void startResizeBounds() { + super.startResizeBounds(); + target.startResizeBounds(); + } + + /** {@inheritDoc} */ + public void endResizeBounds() { + super.endResizeBounds(); + target.endResizeBounds(); + } + + /** + * Since PSWTStickyHandle manager is not visible on screen, it just returns + * false when it is asked to be repainted. + * + * @param pickPath path of this node in which the interaction occurred that + * required the repaint + * + * @return always false + */ + public boolean pickAfterChildren(final PPickPath pickPath) { + return false; + } +} diff --git a/swt/src/main/java/org/piccolo2d/extras/swt/PSWTText.java b/swt/src/main/java/org/piccolo2d/extras/swt/PSWTText.java new file mode 100644 index 0000000..da69a4d --- /dev/null +++ b/swt/src/main/java/org/piccolo2d/extras/swt/PSWTText.java @@ -0,0 +1,528 @@ +/** + * Copyright (C) 1998-1999 by University of Maryland, College Park, MD 20742, USA + * All rights reserved. + */ +package org.piccolo2d.extras.swt; + +import java.awt.Color; +import java.awt.Font; +import java.awt.Graphics2D; +import java.awt.Paint; +import java.awt.geom.AffineTransform; +import java.awt.geom.Point2D; +import java.util.ArrayList; +import java.util.Iterator; + +import org.eclipse.swt.graphics.FontMetrics; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.widgets.Display; +import org.piccolo2d.PNode; +import org.piccolo2d.util.PPaintContext; + + +/** + * PSWTText creates a visual component to support text. Multiple lines + * can be entered, and basic editing is supported. A caret is drawn, and can be + * repositioned with mouse clicks. The text object is positioned so that its + * upper-left corner is at the origin, though this can be changed with the + * translate methods. + */ +public class PSWTText extends PNode { + private static final long serialVersionUID = 1L; + + /** Below this magnification render text as 'greek'. */ + protected static final double DEFAULT_GREEK_THRESHOLD = 5.5; + + /** Default color of text rendered as 'greek'. */ + protected static final Color DEFAULT_GREEK_COLOR = Color.gray; + + /** Default font name of text. */ + protected static final String DEFAULT_FONT_NAME = "Helvetica"; + + /** Default font style for text. */ + protected static final int DEFAULT_FONT_STYLE = Font.PLAIN; + + /** Default font size for text. */ + protected static final int DEFAULT_FONT_SIZE = 12; + + /** Default font for text. */ + protected static final Font DEFAULT_FONT = new Font(DEFAULT_FONT_NAME, DEFAULT_FONT_STYLE, DEFAULT_FONT_SIZE); + + /** Default color for text. */ + protected static final Color DEFAULT_PEN_COLOR = Color.black; + + /** Default text when new text area is created. */ + protected static final String DEFAULT_TEXT = ""; + + /** Default background transparency state. */ + protected static final boolean DEFAULT_IS_TRANSPARENT = false; + + /** Default padding. */ + protected static final int DEFAULT_PADDING = 2; + + /** Whether the text be drawn with a transparent background. */ + private boolean transparent = DEFAULT_IS_TRANSPARENT; + + /** Below this magnification text is rendered as greek. */ + protected double greekThreshold = DEFAULT_GREEK_THRESHOLD; + + /** Color for greek text. */ + protected Color greekColor = DEFAULT_GREEK_COLOR; + + /** Current pen color. */ + protected Color penColor = DEFAULT_PEN_COLOR; + + /** Current text font. */ + protected Font font = DEFAULT_FONT; + + /** The amount of padding on each side of the text. */ + protected int padding = DEFAULT_PADDING; + + /** Each element is one line of text. */ + protected ArrayList lines = new ArrayList(); + + /** Translation offset X. */ + protected double translateX = 0.0; + + /** Translation offset Y. */ + protected double translateY = 0.0; + + /** Default constructor for PSWTTest. */ + public PSWTText() { + this(DEFAULT_TEXT, DEFAULT_FONT); + } + + /** + * PSWTTest constructor with initial text. + * + * @param str The initial text. + */ + public PSWTText(final String str) { + this(str, DEFAULT_FONT); + } + + /** + * PSWTTest constructor with initial text and font. + * + * @param str The initial text. + * @param font The font for this PSWTText component. + */ + public PSWTText(final String str, final Font font) { + setText(str); + this.font = font; + + recomputeBounds(); + } + + /** + * Returns the current pen color. + * + * @return current pen color + */ + public Color getPenColor() { + return penColor; + } + + /** + * Sets the current pen color. + * + * @param color use this color. + */ + public void setPenColor(final Color color) { + penColor = color; + repaint(); + } + + /** + * Returns the current pen paint. + * + * @return the current pen paint + */ + public Paint getPenPaint() { + return penColor; + } + + /** + * Sets the current pen paint. + * + * @param aPaint use this paint. + */ + public void setPenPaint(final Paint aPaint) { + penColor = (Color) aPaint; + } + + /** + * Returns the current background color. + * + * @return the current background color + */ + public Color getBackgroundColor() { + return (Color) getPaint(); + } + + /** + * Sets the current background color. + * + * @param color use this color. + */ + public void setBackgroundColor(final Color color) { + super.setPaint(color); + } + + /** + * Sets whether the text should be drawn in transparent mode, i.e., whether + * the background should be drawn or not. + * + * @param transparent the new transparency of the background + */ + public void setTransparent(final boolean transparent) { + this.transparent = transparent; + } + + /** + * Returns whether the text should be drawn using the transparent mode, + * i.e., whether the background should be drawn or not. + * + * @return true if background will not be drawn + */ + public boolean isTransparent() { + return transparent; + } + + /** + * Returns the current greek threshold. Below this magnification text is + * rendered as 'greek'. + * + * @return magnification at which the text will not be drawn and a blank + * rectangle will appear instead + */ + public double getGreekThreshold() { + return greekThreshold; + } + + /** + * Sets the current greek threshold. Below this magnification text is + * rendered as 'greek'. + * + * @param threshold compared to renderContext magnification. + */ + public void setGreekThreshold(final double threshold) { + greekThreshold = threshold; + repaint(); + } + + /** + * Returns the current font. + * + * @return current font in node + */ + public Font getFont() { + return font; + } + + /** + * Return the text within this text component. Multiline text is returned as + * a single string where each line is separated by a newline character. + * Single line text does not have any newline characters. + * + * @return string containing this node's text + */ + public String getText() { + StringBuffer result = new StringBuffer(); + + final Iterator lineIterator = lines.iterator(); + while (lineIterator.hasNext()) { + result.append(lineIterator.next()); + result.append('\n'); + } + + if (result.length() > 0) { + result.deleteCharAt(result.length() - 1); + } + + return result.toString(); + } + + /** + * Sets the font for the text. + *
+ * Warning: Java has a serious bug in that it does not support very + * small fonts. In particular, fonts that are less than about a pixel high + * just don't work. Since in Jazz, it is common to create objects of + * arbitrary sizes, and then scale them, an application can easily create a + * text object with a very small font by accident. The workaround for this + * bug is to create a larger font for the text object, and then scale the + * node down correspondingly. + * + * @param aFont use this font. + */ + public void setFont(final Font aFont) { + font = aFont; + + recomputeBounds(); + } + + /** + * Sets the text of this visual component to str. Multiple lines of text are + * separated by a newline character. + * + * @param str use this string. + */ + public void setText(final String str) { + int pos = 0; + int index; + boolean done = false; + lines.clear(); + do { + index = str.indexOf('\n', pos); + if (index == -1) { + lines.add(str.substring(pos)); + done = true; + } + else { + lines.add(str.substring(pos, index)); + pos = index + 1; + } + } while (!done); + + recomputeBounds(); + } + + /** + * Set text translation offset X. + * + * @param x the X translation. + */ + public void setTranslateX(final double x) { + setTranslation(x, translateY); + } + + /** + * Get the X offset translation. + * + * @return the X translation. + */ + public double getTranslateX() { + return translateX; + } + + /** + * Set text translation offset Y. + * + * @param y the Y translation. + */ + public void setTranslateY(final double y) { + setTranslation(translateX, y); + } + + /** + * Get the Y offset translation. + * + * @return the Y translation. + */ + public double getTranslateY() { + return translateY; + } + + /** + * Set the text translation offset to the specified position. + * + * @param x the X component of translation + * @param y the Y component of translation + */ + public void setTranslation(final double x, final double y) { + translateX = x; + translateY = y; + + recomputeBounds(); + } + + /** + * Set the text translation offset to point p. + * + * @param p The translation offset. + */ + public void setTranslation(final Point2D p) { + setTranslation(p.getX(), p.getY()); + } + + /** + * Get the text translation offset. + * + * @return The translation offset. + */ + public Point2D getTranslation() { + final Point2D p = new Point2D.Double(translateX, translateY); + return p; + } + + /** + * Renders the text object. + *
+ * The transform, clip, and composite will be set appropriately when this
+ * object is rendered. It is up to this object to restore the transform,
+ * clip, and composite of the Graphics2D if this node changes any of them.
+ * However, the color, font, and stroke are unspecified by Jazz. This object
+ * should set those things if they are used, but they do not need to be
+ * restored.
+ *
+ * @param ppc Contains information about current render.
+ */
+ public void paint(final PPaintContext ppc) {
+ if (lines.isEmpty()) {
+ return;
+ }
+
+ final Graphics2D g2 = ppc.getGraphics();
+ AffineTransform at = null;
+ boolean translated = false;
+
+ if (translateX != 0.0 || translateY != 0.0) {
+ at = g2.getTransform();
+ g2.translate(translateX, translateY);
+ translated = true;
+ }
+
+ final double renderedFontSize = font.getSize() * ppc.getScale();
+
+ // If font is too small then render it as "greek"
+ if (renderedFontSize < greekThreshold) {
+ paintAsGreek(ppc);
+ }
+ else {
+ paintAsText(ppc);
+ }
+
+ if (translated) {
+ g2.setTransform(at);
+ }
+ }
+
+ /**
+ * Paints this object as greek.
+ *
+ * @param ppc The graphics context to paint into.
+ */
+ public void paintAsGreek(final PPaintContext ppc) {
+ final Graphics2D g2 = ppc.getGraphics();
+
+ if (greekColor != null) {
+ g2.setBackground(greekColor);
+ ((SWTGraphics2D) g2).fillRect(0, 0, getWidth(), getHeight());
+ }
+ }
+
+ /**
+ * Paints this object normally (show it's text). Note that the entire text
+ * gets rendered so that it's upper left corner appears at the origin of
+ * this local object.
+ *
+ * @param ppc The graphics context to paint into.
+ */
+ public void paintAsText(final PPaintContext ppc) {
+ final SWTGraphics2D sg2 = (SWTGraphics2D) ppc.getGraphics();
+
+ if (!transparent) {
+ if (getPaint() == null) {
+ sg2.setBackground(Color.WHITE);
+ }
+ else {
+ sg2.setBackground((Color) getPaint());
+ }
+
+ sg2.fillRect(0, 0, (int) getWidth(), (int) getHeight());
+ }
+
+ sg2.translate(padding, padding);
+
+ sg2.setColor(penColor);
+ sg2.setFont(font);
+
+ String line;
+ double y = 0;
+
+ final FontMetrics fontMetrics = sg2.getSWTFontMetrics();
+
+ final Iterator lineIterator = lines.iterator();
+ while (lineIterator.hasNext()) {
+ line = (String) lineIterator.next();
+ if (line.length() != 0) {
+ sg2.drawString(line, 0, y, true);
+ }
+
+ y += fontMetrics.getHeight();
+ }
+
+ sg2.translate(-padding, -padding);
+ }
+
+ /**
+ * Recalculates this node's bounding box by examining it's text content.
+ */
+ protected void recomputeBounds() {
+ final GC gc = new GC(Display.getDefault());
+
+ final Point newBounds;
+ if (isTextEmpty()) {
+ // If no text, then we want to have the bounds of a space character,
+ // so get those bounds here
+ newBounds = gc.stringExtent(" ");
+ }
+ else {
+ newBounds = calculateTextBounds(gc);
+ }
+
+ gc.dispose();
+
+ setBounds(translateX, translateY, newBounds.x + 2 * DEFAULT_PADDING, newBounds.y + 2 * DEFAULT_PADDING);
+ }
+
+ /**
+ * Determines if this node's text is essentially empty.
+ *
+ * @return true if the text is the empty string
+ */
+ private boolean isTextEmpty() {
+ return lines.isEmpty() || lines.size() == 1 && ((String) lines.get(0)).equals("");
+ }
+
+ /**
+ * Calculates the bounds of the text in the box as measured by the given
+ * graphics context and font metrics.
+ *
+ * @param gc graphics context from which the measurements are done
+ * @return point representing the dimensions of the text's bounds
+ */
+ private Point calculateTextBounds(final GC gc) {
+ final SWTGraphics2D g2 = new SWTGraphics2D(gc, Display.getDefault());
+ g2.setFont(font);
+ final FontMetrics fm = g2.getSWTFontMetrics();
+ final Point textBounds = new Point(0, 0);
+
+ boolean firstLine = true;
+
+ final Iterator lineIterator = lines.iterator();
+ while (lineIterator.hasNext()) {
+ String line = (String) lineIterator.next();
+ Point lineBounds = gc.stringExtent(line);
+ if (firstLine) {
+ textBounds.x = lineBounds.x;
+ textBounds.y += fm.getAscent() + fm.getDescent() + fm.getLeading();
+ firstLine = false;
+ }
+ else {
+ textBounds.x = Math.max(lineBounds.x, textBounds.x);
+ textBounds.y += fm.getHeight();
+ }
+ }
+
+ return textBounds;
+ }
+
+ /** {@inheritDoc} */
+ protected void internalUpdateBounds(final double x, final double y, final double width, final double height) {
+ recomputeBounds();
+ }
+
+}
diff --git a/swt/src/main/java/org/piccolo2d/extras/swt/SWTGraphics2D.java b/swt/src/main/java/org/piccolo2d/extras/swt/SWTGraphics2D.java
new file mode 100644
index 0000000..afe84f6
--- /dev/null
+++ b/swt/src/main/java/org/piccolo2d/extras/swt/SWTGraphics2D.java
@@ -0,0 +1,1532 @@
+/*
+ * Copyright (c) 2008-2010, 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.extras.swt;
+
+import java.awt.Color;
+import java.awt.Composite;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.GraphicsConfiguration;
+import java.awt.Image;
+import java.awt.Paint;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.RenderingHints;
+import java.awt.Shape;
+import java.awt.Stroke;
+import java.awt.RenderingHints.Key;
+import java.awt.font.FontRenderContext;
+import java.awt.font.GlyphVector;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Arc2D;
+import java.awt.geom.Ellipse2D;
+import java.awt.geom.NoninvertibleTransformException;
+import java.awt.geom.PathIterator;
+import java.awt.geom.Rectangle2D;
+import java.awt.geom.RoundRectangle2D;
+import java.awt.image.BufferedImage;
+import java.awt.image.BufferedImageOp;
+import java.awt.image.ImageObserver;
+import java.awt.image.RenderedImage;
+import java.awt.image.renderable.RenderableImage;
+import java.text.AttributedCharacterIterator;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Device;
+import org.eclipse.swt.graphics.FontData;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Path;
+import org.eclipse.swt.graphics.Transform;
+
+/**
+ * An extension to Graphics2D to support an SWT Piccolo Canvas with little
+ * modification to the current Piccolo architecture
+ *
+ * There is an outstanding SWT bug request #33319 for more efficient
+ * polyline/polygon rendering methods. It also appears that most of the code
+ * below could be made obselete by bug fix #6490
+ *
+ * A lot of this may also be duplicated in GEF - the eclipse Graphical Editor
+ * Framework
+ *
+ * @author Lance Good
+ */
+public class SWTGraphics2D extends Graphics2D {
+ private static final int DEFAULT_FONT_SIZE = 12;
+
+ private static final boolean DEFAULT_STRING_TRANSPARENCY = true;
+
+ /**
+ * The number of Graphics Contexts active as determined by called to
+ * incrementGCCount and decrementGCCount.
+ */
+ protected static int CACHE_COUNT = 0;
+ /** Map from font names to Fonts. */
+ protected static final HashMap FONT_CACHE = new HashMap();
+ /** Map from awt colors to swt colors. */
+ protected static final HashMap COLOR_CACHE = new HashMap();
+ /** Map from awt shapess to swt Paths. */
+ protected static final HashMap SHAPE_CACHE = new HashMap();
+ /** Buffer used to extract the graphics device. */
+ protected static final BufferedImage BUFFER = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
+
+ private static final Point TEMP_POINT = new Point();
+ private static final Rectangle2D TEMP_RECT = new Rectangle2D.Double();
+ private static final Rectangle2D TEMP_LINE_RECT = new Rectangle2D.Double();
+ private static final org.eclipse.swt.graphics.Rectangle SWT_RECT = new org.eclipse.swt.graphics.Rectangle(0, 0, 0, 0);
+
+ /** The Underlying GraphicsContext provided by swt. */
+ protected GC gc;
+ /** Device onto which all graphics operations will ultimately take place. */
+ protected Device device;
+ /** The current transform to apply to drawing operations. */
+ protected AffineTransform transform = new AffineTransform();
+ private final Transform swtTransform;
+ /** The current font to use when drawing text. */
+ protected org.eclipse.swt.graphics.Font curFont;
+ /** The current stroke width to use when drawing lines. */
+ protected double lineWidth = 1.0;
+
+ /**
+ * Constructor for SWTGraphics2D.
+ *
+ * @param gc The Eclipse Graphics Context onto which all Graphics2D
+ * operations are delegating
+ * @param device Device onto which ultimately all gc operations are drawn
+ * onto
+ */
+ public SWTGraphics2D(final GC gc, final Device device) {
+ this.gc = gc;
+ this.device = device;
+
+ swtTransform = new Transform(device);
+ gc.setAntialias(SWT.ON);
+ }
+
+ // //////////////////
+ // GET CLIP
+ // //////////////////
+
+ /** {@inheritDoc} */
+ public Rectangle getClipBounds() {
+ final org.eclipse.swt.graphics.Rectangle rect = gc.getClipping();
+ final Rectangle aRect = new Rectangle(rect.x, rect.y, rect.width, rect.height);
+ try {
+ SWTShapeManager.transform(aRect, transform.createInverse());
+ }
+ catch (final Exception e) {
+ throw new RuntimeException(e);
+ }
+ return aRect;
+ }
+
+ /** {@inheritDoc} */
+ public void clipRect(final int x, final int y, final int width, final int height) {
+ TEMP_RECT.setRect(x, y, width, height);
+ SWTShapeManager.transform(TEMP_RECT, transform);
+ SWTShapeManager.awtToSWT(TEMP_RECT, SWT_RECT);
+
+ org.eclipse.swt.graphics.Rectangle clip = gc.getClipping();
+ clip = clip.intersection(SWT_RECT);
+
+ gc.setClipping(clip);
+ }
+
+ /** {@inheritDoc} */
+ public void setClip(final int x, final int y, final int width, final int height) {
+ TEMP_RECT.setRect(x, y, width, height);
+ SWTShapeManager.transform(TEMP_RECT, transform);
+ SWTShapeManager.awtToSWT(TEMP_RECT, SWT_RECT);
+
+ gc.setClipping(SWT_RECT);
+ }
+
+ /**
+ * This method isn't really supported by SWT - so will use the shape bounds.
+ *
+ * @param s shape of the clipping region to apply to graphics operations
+ */
+ public void clip(final Shape s) {
+ final Rectangle2D clipBds = s.getBounds2D();
+ SWTShapeManager.transform(clipBds, transform);
+ SWTShapeManager.awtToSWT(clipBds, SWT_RECT);
+
+ org.eclipse.swt.graphics.Rectangle clip = gc.getClipping();
+ clip = clip.intersection(SWT_RECT);
+
+ gc.setClipping(SWT_RECT);
+ }
+
+ /**
+ * This method isn't really supported by SWT - so will use the shape bounds.
+ *
+ * @param clip the desired clipping region's shape, will be simplified to
+ * its bounds
+ */
+ public void setClip(final Shape clip) {
+ if (clip == null) {
+ gc.setClipping((org.eclipse.swt.graphics.Rectangle) null);
+ }
+ else {
+ final Rectangle2D clipBds = clip.getBounds2D();
+ SWTShapeManager.transform(clipBds, transform);
+ SWTShapeManager.awtToSWT(clipBds, SWT_RECT);
+
+ gc.setClipping(SWT_RECT);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public Shape getClip() {
+ final org.eclipse.swt.graphics.Rectangle rect = gc.getClipping();
+ final Rectangle2D aRect = new Rectangle2D.Double(rect.x, rect.y, rect.width, rect.height);
+ try {
+ SWTShapeManager.transform(aRect, transform.createInverse());
+ }
+ catch (final NoninvertibleTransformException e) {
+ throw new RuntimeException(e);
+ }
+ return aRect;
+ }
+
+ /**
+ * Returns a dummy device configuration.
+ *
+ * @return a dummy device configuration
+ */
+ public GraphicsConfiguration getDeviceConfiguration() {
+ return ((Graphics2D) BUFFER.getGraphics()).getDeviceConfiguration();
+ }
+
+ // //////////////
+ // COLOR METHODS
+ // //////////////
+
+ /** {@inheritDoc} */
+ public Paint getPaint() {
+ return getColor();
+ }
+
+ /** {@inheritDoc} */
+ public void setPaint(final Paint paint) {
+ if (paint instanceof Color) {
+ setColor((Color) paint);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public Color getColor() {
+ final org.eclipse.swt.graphics.Color color = gc.getForeground();
+ final Color awtColor = new Color(color.getRed(), color.getGreen(), color.getBlue());
+ return awtColor;
+ }
+
+ /** {@inheritDoc} */
+ public void setColor(final Color c) {
+ org.eclipse.swt.graphics.Color cachedColor = (org.eclipse.swt.graphics.Color) COLOR_CACHE.get(c);
+ if (cachedColor == null) {
+ cachedColor = new org.eclipse.swt.graphics.Color(device, c.getRed(), c.getGreen(), c.getBlue());
+ COLOR_CACHE.put(c, cachedColor);
+ }
+ gc.setForeground(cachedColor);
+ }
+
+ /**
+ * Sets the foreground color to the provided swt color.
+ *
+ * @param foregroundColor new foreground color
+ */
+ public void setColor(final org.eclipse.swt.graphics.Color foregroundColor) {
+ gc.setForeground(foregroundColor);
+ }
+
+ /** {@inheritDoc} */
+ public void setBackground(final Color c) {
+ org.eclipse.swt.graphics.Color cachedColor = (org.eclipse.swt.graphics.Color) COLOR_CACHE.get(c);
+ if (cachedColor == null) {
+ cachedColor = new org.eclipse.swt.graphics.Color(device, c.getRed(), c.getGreen(), c.getBlue());
+ COLOR_CACHE.put(c, cachedColor);
+ }
+ gc.setBackground(cachedColor);
+ }
+
+ /**
+ * Sets the background color to the provided swt color.
+ *
+ * @param backgroundColor new background color
+ */
+ public void setBackground(final org.eclipse.swt.graphics.Color backgroundColor) {
+ gc.setBackground(backgroundColor);
+ }
+
+ /** {@inheritDoc} */
+ public Color getBackground() {
+ final org.eclipse.swt.graphics.Color color = gc.getBackground();
+ final Color awtColor = new Color(color.getRed(), color.getGreen(), color.getBlue());
+ return awtColor;
+ }
+
+ // //////////////
+ // FONT METHODS
+ // //////////////
+
+ /**
+ * Returns the current swt font to use when drawing.
+ *
+ * @return current swt font
+ */
+ public org.eclipse.swt.graphics.Font getSWTFont() {
+ return curFont;
+ }
+
+ /**
+ * Returns the font metrics of the current SWT font.
+ *
+ * @return font metrics of the current SWT font
+ */
+ public org.eclipse.swt.graphics.FontMetrics getSWTFontMetrics() {
+ gc.setFont(curFont);
+ return gc.getFontMetrics();
+ }
+
+ /** {@inheritDoc} */
+ public Font getFont() {
+ if (curFont != null) {
+ int style = Font.PLAIN;
+
+ final FontData[] fd = curFont.getFontData();
+ if (fd.length > 0) {
+ if ((fd[0].getStyle() & SWT.BOLD) != 0) {
+ style = style | Font.BOLD;
+ }
+ if ((fd[0].getStyle() & SWT.ITALIC) != 0) {
+ style = style | SWT.ITALIC;
+ }
+
+ return new Font(fd[0].getName(), style, fd[0].getHeight());
+ }
+ return null;
+ }
+ else {
+ return null;
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void setFont(final Font font) {
+ // TODO: prevent NPE
+ final String fontString = "name=" + font.getFamily() + ";bold=" + font.isBold() + ";italic=" + font.isItalic()
+ + ";size=" + font.getSize();
+
+ curFont = getFont(fontString);
+ }
+
+ /**
+ * Set the font for this SWTGraphics2D to font
.
+ *
+ * @param font font for this SWTGraphics2D
+ */
+ public void setFont(final org.eclipse.swt.graphics.Font font) {
+ curFont = font;
+ }
+
+ /**
+ * Returns the SWT font matching the given font string.
+ *
+ * @param fontString description of the font desired
+ * @return matching font, or null if not found
+ */
+ public org.eclipse.swt.graphics.Font getFont(final String fontString) {
+ org.eclipse.swt.graphics.Font cachedFont = (org.eclipse.swt.graphics.Font) FONT_CACHE.get(fontString);
+ if (cachedFont == null) {
+ int style = 0;
+ if (fontString.indexOf("bold=true") != -1) {
+ style = style | SWT.BOLD;
+ }
+ if (fontString.indexOf("italic=true") != -1) {
+ style = style | SWT.ITALIC;
+ }
+
+ final String name = fontString.substring(0, fontString.indexOf(";"));
+ final String size = fontString.substring(fontString.lastIndexOf(";") + 1, fontString.length());
+ int sizeInt = DEFAULT_FONT_SIZE;
+ try {
+ sizeInt = Integer.parseInt(size.substring(size.indexOf("=") + 1, size.length()));
+ }
+ catch (final Exception e) {
+ throw new RuntimeException(e);
+ }
+
+ cachedFont = new org.eclipse.swt.graphics.Font(device,
+ name.substring(name.indexOf("=") + 1, name.length()), sizeInt, style);
+ FONT_CACHE.put(fontString, cachedFont);
+ }
+ return cachedFont;
+ }
+
+ // /////////////////////////
+ // AFFINE TRANSFORM METHODS
+ // /////////////////////////
+
+ /** {@inheritDoc} */
+ public void translate(final int x, final int y) {
+ transform.translate(x, y);
+ updateSWTTransform();
+ }
+
+ /** {@inheritDoc} */
+ public void translate(final double tx, final double ty) {
+ transform.translate(tx, ty);
+ updateSWTTransform();
+ }
+
+ /** {@inheritDoc} */
+ public void rotate(final double theta) {
+ transform.rotate(theta);
+ updateSWTTransform();
+ }
+
+ /** {@inheritDoc} */
+ public void rotate(final double theta, final double x, final double y) {
+ transform.rotate(theta, x, y);
+ updateSWTTransform();
+ }
+
+ /** {@inheritDoc} */
+ public void scale(final double sx, final double sy) {
+ transform.scale(sx, sy);
+ updateSWTTransform();
+ }
+
+ /** {@inheritDoc} */
+ public void shear(final double shx, final double shy) {
+ transform.shear(shx, shy);
+ updateSWTTransform();
+ }
+
+ /** {@inheritDoc} */
+ public void transform(final AffineTransform srcTransform) {
+ transform.concatenate(srcTransform);
+ updateSWTTransform();
+ }
+
+ /** {@inheritDoc} */
+ public void setTransform(final AffineTransform newTransform) {
+ transform = (AffineTransform) newTransform.clone();
+ updateSWTTransform();
+ }
+
+ /** {@inheritDoc} */
+ public AffineTransform getTransform() {
+ return (AffineTransform) transform.clone();
+ }
+
+ // SUPPORT METHODS
+ // /////////////////////////////
+
+ /**
+ * Updates the SWT transform instance such that it matches AWTs counterpart.
+ */
+ private void updateSWTTransform() {
+ final double[] m = new double[6];
+ transform.getMatrix(m);
+ swtTransform.setElements((float) m[0], (float) m[1], (float) m[2], (float) m[3], (float) m[4], (float) m[5]);
+ }
+
+ /**
+ * Converts a java 2d path iterator to a SWT path.
+ *
+ * @param iter specifies the iterator to be converted.
+ * @return the corresponding path object. Must be disposed() when no longer
+ * used.
+ */
+ private Path pathIterator2Path(final PathIterator iter) {
+ final float[] coords = new float[6];
+
+ final Path path = new Path(device);
+
+ while (!iter.isDone()) {
+ final int type = iter.currentSegment(coords);
+
+ switch (type) {
+ case PathIterator.SEG_MOVETO:
+ path.moveTo(coords[0], coords[1]);
+ break;
+
+ case PathIterator.SEG_LINETO:
+ path.lineTo(coords[0], coords[1]);
+ break;
+
+ case PathIterator.SEG_CLOSE:
+ path.close();
+ break;
+
+ case PathIterator.SEG_QUADTO:
+ path.quadTo(coords[0], coords[1], coords[2], coords[3]);
+ break;
+
+ case PathIterator.SEG_CUBICTO:
+ path.cubicTo(coords[0], coords[1], coords[2], coords[3], coords[4], coords[5]);
+ break;
+ default:
+ // log this?
+ }
+
+ iter.next();
+ }
+ return path;
+ }
+
+ /** {@inheritDoc} */
+ public void clearRect(final int x, final int y, final int width, final int height) {
+ fillRect(x, y, width, height);
+ }
+
+ /** {@inheritDoc} */
+ public void draw(final Shape s) {
+ if (s instanceof Rectangle2D) {
+ final Rectangle2D r2 = (Rectangle2D) s;
+ drawRect(r2.getX(), r2.getY(), r2.getWidth(), r2.getHeight());
+ }
+ else if (s instanceof Ellipse2D) {
+ final Ellipse2D e2 = (Ellipse2D) s;
+ drawOval(e2.getX(), e2.getY(), e2.getWidth(), e2.getHeight());
+ }
+ else if (s instanceof RoundRectangle2D) {
+ final RoundRectangle2D r2 = (RoundRectangle2D) s;
+ drawRoundRect(r2.getX(), r2.getY(), r2.getWidth(), r2.getHeight(), r2.getArcWidth(), r2.getArcHeight());
+ }
+ else if (s instanceof Arc2D) {
+ final Arc2D a2 = (Arc2D) s;
+ drawArc(a2.getX(), a2.getY(), a2.getWidth(), a2.getHeight(), a2.getAngleStart(), a2.getAngleExtent());
+ }
+ else {
+ Path p = (Path) SHAPE_CACHE.get(s);
+ if (p == null) {
+ p = pathIterator2Path(s.getPathIterator(null));
+ SHAPE_CACHE.put(s, p);
+ }
+ drawPath(p);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void fill(final Shape s) {
+ if (s instanceof Rectangle2D) {
+ final Rectangle2D r2 = (Rectangle2D) s;
+ fillRect(r2.getX(), r2.getY(), r2.getWidth(), r2.getHeight());
+ }
+ else if (s instanceof Ellipse2D) {
+ final Ellipse2D e2 = (Ellipse2D) s;
+ fillOval(e2.getX(), e2.getY(), e2.getWidth(), e2.getHeight());
+ }
+ else if (s instanceof RoundRectangle2D) {
+ final RoundRectangle2D r2 = (RoundRectangle2D) s;
+ fillRoundRect(r2.getX(), r2.getY(), r2.getWidth(), r2.getHeight(), r2.getArcWidth(), r2.getArcHeight());
+ }
+ else if (s instanceof Arc2D) {
+ final Arc2D a2 = (Arc2D) s;
+ fillArc(a2.getX(), a2.getY(), a2.getWidth(), a2.getHeight(), a2.getAngleStart(), a2.getAngleExtent());
+ }
+ else {
+ Path p = (Path) SHAPE_CACHE.get(s);
+ if (p == null) {
+ p = pathIterator2Path(s.getPathIterator(null));
+ SHAPE_CACHE.put(s, p);
+ }
+ drawPath(p);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void drawPolyline(final int[] xPoints, final int[] yPoints, final int nPoints) {
+ final int[] ptArray = new int[2 * nPoints];
+ for (int i = 0; i < nPoints; i++) {
+ TEMP_POINT.setLocation(xPoints[i], yPoints[i]);
+ transform.transform(TEMP_POINT, TEMP_POINT);
+ ptArray[2 * i] = xPoints[i];
+ ptArray[2 * i + 1] = yPoints[i];
+ }
+
+ gc.setLineWidth(getTransformedLineWidth());
+ gc.drawPolyline(ptArray);
+ }
+
+ /**
+ * Draw a polyline from the specified double array of points.
+ *
+ * @param pts double array of points
+ */
+ public void drawPolyline(final double[] pts) {
+ final int[] intPts = SWTShapeManager.transform(pts, transform);
+ gc.drawPolyline(intPts);
+ }
+
+ /** {@inheritDoc} */
+ public void drawPolygon(final int[] xPoints, final int[] yPoints, final int nPoints) {
+ final int[] ptArray = new int[2 * nPoints];
+ for (int i = 0; i < nPoints; i++) {
+ TEMP_POINT.setLocation(xPoints[i], yPoints[i]);
+ transform.transform(TEMP_POINT, TEMP_POINT);
+ ptArray[2 * i] = xPoints[i];
+ ptArray[2 * i + 1] = yPoints[i];
+ }
+
+ gc.drawPolygon(ptArray);
+ }
+
+ /**
+ * Fill a polyline from the specified double array of points.
+ *
+ * @param pts double array of points
+ */
+ public void fillPolygon(final double[] pts) {
+ final int[] intPts = SWTShapeManager.transform(pts, transform);
+ gc.fillPolygon(intPts);
+ }
+
+ /** {@inheritDoc} */
+ public void fillPolygon(final int[] xPoints, final int[] yPoints, final int nPoints) {
+ final int[] ptArray = new int[2 * nPoints];
+ for (int i = 0; i < nPoints; i++) {
+ TEMP_POINT.setLocation(xPoints[i], yPoints[i]);
+ transform.transform(TEMP_POINT, TEMP_POINT);
+ ptArray[2 * i] = xPoints[i];
+ ptArray[2 * i + 1] = yPoints[i];
+ }
+
+ gc.fillPolygon(ptArray);
+ }
+
+ /** {@inheritDoc} */
+ public void drawLine(final int x1, final int y1, final int x2, final int y2) {
+ drawLine((double) x1, (double) y1, (double) x2, (double) y2);
+ }
+
+ /**
+ * Draws a line, using the current color, between the points (x1, y1) and
+ * (x2, y2) in this graphics context's coordinate system.
+ *
+ * @param x1 the first point's x coordinate.
+ * @param y1 the first point's y coordinate.
+ * @param x2 the second point's x coordinate.
+ * @param y2 the second point's y coordinate.
+ */
+ public void drawLine(final double x1, final double y1, final double x2, final double y2) {
+ TEMP_POINT.setLocation(x1, y1);
+ transform.transform(TEMP_POINT, TEMP_POINT);
+ final double transformedX1 = (int) TEMP_POINT.getX();
+ final double transformedY1 = (int) TEMP_POINT.getY();
+ TEMP_POINT.setLocation(x2, y2);
+ transform.transform(TEMP_POINT, TEMP_POINT);
+ final double transformedX2 = (int) TEMP_POINT.getX();
+ final double transformedY2 = (int) TEMP_POINT.getY();
+
+ gc.setLineWidth(getTransformedLineWidth());
+ gc.drawLine((int) (transformedX1 + 0.5), (int) (transformedY1 + 0.5), (int) (transformedX2 + 0.5),
+ (int) (transformedY2 + 0.5));
+ }
+
+ // **************************************************************************
+ // *
+ // FOR NOW - ASSUME NO ROTATION ON THE TRANSFORM FOR THE FOLLOWING CALLS!
+ // **************************************************************************
+ // *
+
+ /**
+ * Copies the image to the specified position.
+ *
+ * @param img swt image to be copied
+ * @param x x component of position
+ * @param y y component of position
+ */
+ public void copyArea(final org.eclipse.swt.graphics.Image img, final double x, final double y) {
+ TEMP_POINT.setLocation(x, y);
+ transform.transform(TEMP_POINT, TEMP_POINT);
+
+ gc.copyArea(img, (int) (TEMP_POINT.getX() + 0.5), (int) (TEMP_POINT.getY() + 0.5));
+ }
+
+ /** {@inheritDoc} */
+ public void copyArea(final int x, final int y, final int width, final int height, final int dx, final int dy) {
+ TEMP_RECT.setRect(x, y, width, height);
+ SWTShapeManager.transform(TEMP_RECT, transform);
+
+ TEMP_POINT.setLocation(dx, dy);
+ transform.transform(TEMP_POINT, TEMP_POINT);
+ gc.copyArea((int) TEMP_RECT.getX(), (int) TEMP_RECT.getY(), (int) TEMP_RECT.getWidth(), (int) TEMP_RECT
+ .getHeight(), (int) TEMP_POINT.getX(), (int) TEMP_POINT.getY());
+ }
+
+ /**
+ * Renders the text of the specified String, using the current text
+ * attribute state in the Graphics2D context. The baseline of the first
+ * character is at position (x, y) in the User Space. The rendering
+ * attributes applied include the Clip, Transform, Paint, Font and Composite
+ * attributes. For characters in script systems such as Hebrew and Arabic,
+ * the glyphs can be rendered from right to left, in which case the
+ * coordinate supplied is the location of the leftmost character on the
+ * baseline.
+ *
+ * @param str the string to be rendered
+ * @param x the x coordinate of the location where the String should be
+ * rendered
+ * @param y the y coordinate of the location where the String should be
+ * rendered
+ * @param isTransparent whether a background should be painted behind the
+ * text
+ */
+ public void drawString(final String str, final int x, final int y, final boolean isTransparent) {
+ gc.setTransform(swtTransform);
+ gc.drawString(str, x, y, isTransparent);
+ gc.setTransform(null);
+ }
+
+ /** {@inheritDoc} */
+ public void drawString(final String str, final int x, final int y) {
+ drawString(str, x, y, DEFAULT_STRING_TRANSPARENCY);
+ }
+
+ /**
+ * Renders the text of the specified String, using the current text
+ * attribute state in the Graphics2D context. The baseline of the first
+ * character is at position (x, y) in the User Space. The rendering
+ * attributes applied include the Clip, Transform, Paint, Font and Composite
+ * attributes. For characters in script systems such as Hebrew and Arabic,
+ * the glyphs can be rendered from right to left, in which case the
+ * coordinate supplied is the location of the leftmost character on the
+ * baseline.
+ *
+ * @param str the string to be rendered
+ * @param x the x coordinate of the location where the String should be
+ * rendered
+ * @param y the y coordinate of the location where the String should be
+ * rendered
+ */
+ public void drawString(final String str, final double x, final double y) {
+ drawString(str, (int) (x + 0.5), (int) (y + 0.5));
+ }
+
+ /**
+ * Renders the text of the specified String, using the current text
+ * attribute state in the Graphics2D context. The baseline of the first
+ * character is at position (x, y) in the User Space. The rendering
+ * attributes applied include the Clip, Transform, Paint, Font and Composite
+ * attributes. For characters in script systems such as Hebrew and Arabic,
+ * the glyphs can be rendered from right to left, in which case the
+ * coordinate supplied is the location of the leftmost character on the
+ * baseline.
+ *
+ * @param str the string to be rendered
+ * @param x the x coordinate of the location where the String should be
+ * rendered
+ * @param y the y coordinate of the location where the String should be
+ * rendered
+ * @param isTransparent whether a background should be painted behind the
+ * text
+ */
+ public void drawString(final String str, final double x, final double y, final boolean isTransparent) {
+ drawString(str, (int) (x + 0.5), (int) (y + 0.5), isTransparent);
+ }
+
+ /** {@inheritDoc} */
+ public void drawString(final String str, final float x, final float y) {
+ drawString(str, (int) (x + 0.5), (int) (y + 0.5));
+ }
+
+ /**
+ * Renders the text of the specified String, using the current text
+ * attribute state in the Graphics2D context. The baseline of the first
+ * character is at position (x, y) in the User Space. The rendering
+ * attributes applied include the Clip, Transform, Paint, Font and Composite
+ * attributes. For characters in script systems such as Hebrew and Arabic,
+ * the glyphs can be rendered from right to left, in which case the
+ * coordinate supplied is the location of the leftmost character on the
+ * baseline.
+ *
+ * @param str the string to be rendered
+ * @param x the x coordinate of the location where the String should be
+ * rendered
+ * @param y the y coordinate of the location where the String should be
+ * rendered
+ */
+ public void drawText(final String str, final double x, final double y) {
+ drawString(str, (int) (x + 0.5), (int) (y + 0.5));
+ }
+
+ /**
+ * Renders the text of the specified String, using the current text
+ * attribute state in the Graphics2D context. The baseline of the first
+ * character is at position (x, y) in the User Space. The rendering
+ * attributes applied include the Clip, Transform, Paint, Font and Composite
+ * attributes. For characters in script systems such as Hebrew and Arabic,
+ * the glyphs can be rendered from right to left, in which case the
+ * coordinate supplied is the location of the leftmost character on the
+ * baseline.
+ *
+ * @param str the string to be rendered
+ * @param x the x coordinate of the location where the String should be
+ * rendered
+ * @param y the y coordinate of the location where the String should be
+ * rendered
+ * @param flags flags to apply to the string as defined by SWT
+ */
+ public void drawText(final String str, final double x, final double y, final int flags) {
+ drawText(str, (int) (x + 0.5), (int) (y + 0.5), flags);
+ }
+
+ /**
+ * Renders the text of the specified String, using the current text
+ * attribute state in the Graphics2D context. The baseline of the first
+ * character is at position (x, y) in the User Space. The rendering
+ * attributes applied include the Clip, Transform, Paint, Font and Composite
+ * attributes. For characters in script systems such as Hebrew and Arabic,
+ * the glyphs can be rendered from right to left, in which case the
+ * coordinate supplied is the location of the leftmost character on the
+ * baseline.
+ *
+ * @param str the string to be rendered
+ * @param x the x coordinate of the location where the String should be
+ * rendered
+ * @param y the y coordinate of the location where the String should be
+ * rendered
+ * @param flags flags to apply to the string as defined by SWT
+ */
+ public void drawText(final String str, final int x, final int y, final int flags) {
+ gc.setTransform(swtTransform);
+ gc.drawText(str, x, y, flags);
+ gc.setTransform(null);
+ }
+
+ /** {@inheritDoc} */
+ public void drawRect(final int x, final int y, final int width, final int height) {
+ drawRect((double) x, (double) y, (double) width, (double) height);
+ }
+
+ /**
+ * Draws the outline of the specified rectangle. The left and right edges of
+ * the rectangle are at x and x + width. The top and bottom edges are at y
+ * and y + height. The rectangle is drawn using the graphics context's
+ * current color.
+ *
+ * @param x the x coordinate of the rectangle to be drawn.
+ * @param y the y coordinate of the rectangle to be drawn.
+ * @param width the width of the rectangle to be drawn.
+ * @param height the height of the rectangle to be drawn.
+ */
+ public void drawRect(final double x, final double y, final double width, final double height) {
+ TEMP_RECT.setRect(x, y, width, height);
+ SWTShapeManager.transform(TEMP_RECT, transform);
+ SWTShapeManager.awtToSWT(TEMP_RECT, SWT_RECT);
+
+ gc.setLineWidth(getTransformedLineWidth());
+ gc.drawRectangle(SWT_RECT);
+ }
+
+ /** {@inheritDoc} */
+ public void fillRect(final int x, final int y, final int width, final int height) {
+ fillRect((double) x, (double) y, (double) width, (double) height);
+ }
+
+ /**
+ * Fills the specified rectangle. The left and right edges of the rectangle
+ * are at x and x + width - 1. The top and bottom edges are at y and y +
+ * height - 1. The resulting rectangle covers an area width pixels wide by
+ * height pixels tall. The rectangle is filled using the graphics context's
+ * current color.
+ *
+ * @param x the x coordinate of the rectangle to be filled.
+ * @param y the y coordinate of the rectangle to be filled.
+ * @param width the width of the rectangle to be filled.
+ * @param height the height of the rectangle to be filled.
+ */
+ public void fillRect(final double x, final double y, final double width, final double height) {
+ TEMP_RECT.setRect(x, y, width, height);
+ SWTShapeManager.transform(TEMP_RECT, transform);
+ SWTShapeManager.awtToSWT(TEMP_RECT, SWT_RECT);
+
+ gc.fillRectangle(SWT_RECT);
+ }
+
+ /** {@inheritDoc} */
+ public void drawRoundRect(final int x, final int y, final int width, final int height, final int arcWidth,
+ final int arcHeight) {
+ drawRoundRect((double) x, (double) y, (double) width, (double) height, (double) arcWidth, (double) arcHeight);
+ }
+
+ /**
+ * Draws an outlined round-cornered rectangle using this graphics context's
+ * current color. The left and right edges of the rectangle are at x and x +
+ * width, respectively. The top and bottom edges of the rectangle are at y
+ * and y + height.
+ *
+ * @param x the x coordinate of the rectangle to be drawn.
+ * @param y the y coordinate of the rectangle to be drawn.
+ * @param width the width of the rectangle to be drawn.
+ * @param height the height of the rectangle to be drawn.
+ * @param arcWidth the horizontal diameter of the arc at the four corners.
+ * @param arcHeight the vertical diameter of the arc at the four corners.
+ */
+ public void drawRoundRect(final double x, final double y, final double width, final double height,
+ final double arcWidth, final double arcHeight) {
+ TEMP_RECT.setRect(x, y, width, height);
+ SWTShapeManager.transform(TEMP_RECT, transform);
+ final double tx = TEMP_RECT.getX();
+ final double ty = TEMP_RECT.getY();
+ final double twidth = TEMP_RECT.getWidth();
+ final double theight = TEMP_RECT.getHeight();
+
+ TEMP_RECT.setRect(0, 0, arcWidth, arcHeight);
+ SWTShapeManager.transform(TEMP_RECT, transform);
+ final double tarcWidth = TEMP_RECT.getWidth();
+ final double tarcHeight = TEMP_RECT.getHeight();
+
+ gc.setLineWidth(getTransformedLineWidth());
+ gc.drawRoundRectangle((int) (tx + 0.5), (int) (ty + 0.5), (int) (twidth + 0.5), (int) (theight + 0.5),
+ (int) (tarcWidth + 0.5), (int) (tarcHeight + 0.5));
+ }
+
+ /** {@inheritDoc} */
+ public void fillRoundRect(final int x, final int y, final int width, final int height, final int arcWidth,
+ final int arcHeight) {
+ fillRoundRect((double) x, (double) y, (double) width, (double) height, (double) arcWidth, (double) arcHeight);
+ }
+
+ /**
+ * Fills the specified rounded corner rectangle with the current color. The
+ * left and right edges of the rectangle are at x and x + width - 1,
+ * respectively. The top and bottom edges of the rectangle are at y and y +
+ * height - 1.
+ *
+ *@param x the x coordinate of the rectangle to be filled.
+ *@param y the y coordinate of the rectangle to be filled.
+ *@param width the width of the rectangle to be filled.
+ *@param height the height of the rectangle to be filled.
+ *@param arcWidth the horizontal diameter of the arc at the four corners.
+ *@param arcHeight the vertical diameter of the arc at the four corners.
+ */
+ public void fillRoundRect(final double x, final double y, final double width, final double height,
+ final double arcWidth, final double arcHeight) {
+ TEMP_RECT.setRect(x, y, width, height);
+ SWTShapeManager.transform(TEMP_RECT, transform);
+ final double tx = TEMP_RECT.getX();
+ final double ty = TEMP_RECT.getY();
+ final double twidth = TEMP_RECT.getWidth();
+ final double theight = TEMP_RECT.getHeight();
+
+ TEMP_RECT.setRect(0, 0, arcWidth, arcHeight);
+ SWTShapeManager.transform(TEMP_RECT, transform);
+ final double tarcWidth = TEMP_RECT.getWidth();
+ final double tarcHeight = TEMP_RECT.getHeight();
+
+ gc.setLineWidth(getTransformedLineWidth());
+ gc.fillRoundRectangle((int) (tx + 0.5), (int) (ty + 0.5), (int) (twidth + 0.5), (int) (theight + 0.5),
+ (int) (tarcWidth + 0.5), (int) (tarcHeight + 0.5));
+ }
+
+ /** {@inheritDoc} */
+ public void drawOval(final int x, final int y, final int width, final int height) {
+ drawOval((double) x, (double) y, (double) width, (double) height);
+ }
+
+ /**
+ * Draws the outline of an oval. The result is a circle or ellipse that fits
+ * within the rectangle specified by the x, y, width, and height arguments.
+ * The oval covers an area that is width + 1 pixels wide and height + 1
+ * pixels tall.
+ *
+ * @param x the x coordinate of the upper left corner of the oval to be
+ * drawn.
+ * @param y the y coordinate of the upper left corner of the oval to be
+ * drawn.
+ * @param width the width of the oval to be drawn.
+ * @param height the height of the oval to be drawn.
+ */
+ public void drawOval(final double x, final double y, final double width, final double height) {
+ TEMP_RECT.setRect(x, y, width, height);
+ SWTShapeManager.transform(TEMP_RECT, transform);
+
+ gc.setLineWidth(getTransformedLineWidth());
+ gc.drawOval((int) (TEMP_RECT.getX() + 0.5), (int) (TEMP_RECT.getY() + 0.5), (int) (TEMP_RECT.getWidth() + 0.5),
+ (int) (TEMP_RECT.getHeight() + 0.5));
+ }
+
+ /** {@inheritDoc} */
+ public void fillOval(final int x, final int y, final int width, final int height) {
+ fillOval((double) x, (double) y, (double) width, (double) height);
+ }
+
+ /**
+ * Fills an oval bounded by the specified rectangle with the current color.
+ *
+ * @param x the x coordinate of the upper left corner of the oval to be
+ * filled.
+ * @param y the y coordinate of the upper left corner of the oval to be
+ * filled.
+ * @param width the width of the oval to be filled.
+ * @param height the height of the oval to be filled.
+ */
+ public void fillOval(final double x, final double y, final double width, final double height) {
+ TEMP_RECT.setRect(x, y, width, height);
+ SWTShapeManager.transform(TEMP_RECT, transform);
+
+ gc.fillOval((int) (TEMP_RECT.getX() + 0.5), (int) (TEMP_RECT.getY() + 0.5), (int) (TEMP_RECT.getWidth() + 0.5),
+ (int) (TEMP_RECT.getHeight() + 0.5));
+ }
+
+ /** {@inheritDoc} */
+ public void drawArc(final int x, final int y, final int width, final int height, final int startAngle,
+ final int extent) {
+ drawArc((double) x, (double) y, (double) width, (double) height, (double) startAngle, (double) extent);
+ }
+
+ /**
+ * Draws the outline of a circular or elliptical arc covering the specified
+ * rectangle.
+ *
+ * The resulting arc begins at startAngle and extends for arcAngle degrees,
+ * using the current color. Angles are interpreted such that 0 degrees is at
+ * the 3 o'clock position. A positive value indicates a counter-clockwise
+ * rotation while a negative value indicates a clockwise rotation.
+ *
+ * The center of the arc is the center of the rectangle whose origin is (x,
+ * y) and whose size is specified by the width and height arguments.
+ *
+ * The resulting arc covers an area width + 1 pixels wide by height + 1
+ * pixels tall.
+ *
+ * The angles are specified relative to the non-square extents of the
+ * bounding rectangle such that 45 degrees always falls on the line from the
+ * center of the ellipse to the upper right corner of the bounding
+ * rectangle. As a result, if the bounding rectangle is noticeably longer in
+ * one axis than the other, the angles to the start and end of the arc
+ * segment will be skewed farther along the longer axis of the bounds.
+ *
+ * @param x the x coordinate of the upper-left corner of the arc to be
+ * drawn.
+ * @param y the y coordinate of the upper-left corner of the arc to be
+ * drawn.
+ * @param width the width of the arc to be drawn.
+ * @param height the height of the arc to be drawn.
+ * @param startAngle the beginning angle.
+ * @param extent the angular extent of the arc, relative to the start angle.
+ */
+ public void drawArc(final double x, final double y, final double width, final double height,
+ final double startAngle, final double extent) {
+ TEMP_RECT.setRect(x, y, width, height);
+ SWTShapeManager.transform(TEMP_RECT, transform);
+
+ gc.setLineWidth(getTransformedLineWidth());
+ gc.drawArc((int) (TEMP_RECT.getX() + 0.5), (int) (TEMP_RECT.getY() + 0.5), (int) (TEMP_RECT.getWidth() + 0.5),
+ (int) (TEMP_RECT.getHeight() + 0.5), (int) (startAngle + 0.5), (int) (startAngle + extent + 0.5));
+ }
+
+ /** {@inheritDoc} */
+ public void fillArc(final int x, final int y, final int width, final int height, final int startAngle,
+ final int extent) {
+ drawArc((double) x, (double) y, (double) width, (double) height, (double) startAngle, (double) extent);
+ }
+
+ /**
+ * Draws a filledArc with the options provided.
+ *
+ * @param x the x coordinate of the upper-left corner of the arc to be
+ * filled.
+ * @param y the y coordinate of the upper-left corner of the arc to be
+ * filled.
+ * @param width the width of the arc to be filled.
+ * @param height the height of the arc to be filled.
+ * @param startAngle the beginning angle.
+ * @param extent the angular extent of the arc, relative to the start angle.
+ */
+ public void fillArc(final double x, final double y, final double width, final double height,
+ final double startAngle, final double extent) {
+ TEMP_RECT.setRect(x, y, width, height);
+ SWTShapeManager.transform(TEMP_RECT, transform);
+
+ gc.drawArc((int) (TEMP_RECT.getX() + 0.5), (int) (TEMP_RECT.getY() + 0.5), (int) (TEMP_RECT.getWidth() + 0.5),
+ (int) (TEMP_RECT.getHeight() + 0.5), (int) (startAngle + 0.5), (int) (startAngle + extent + 0.5));
+ }
+
+ /**
+ * Draws the provided path.
+ *
+ * @param p path to draw
+ */
+ public void drawPath(final Path p) {
+ gc.setTransform(swtTransform);
+ gc.drawPath(p);
+ gc.setTransform(null);
+ }
+
+ /**
+ * Draws a filled version of the provided path.
+ *
+ * @param p path to draw filled
+ */
+ public void fillPath(final Path p) {
+ gc.setTransform(swtTransform);
+ gc.fillPath(p);
+ gc.setTransform(null);
+ }
+
+ /**
+ * Draws the provided image at the position specified.
+ *
+ * @param image image to draw
+ * @param x x component of the position
+ * @param y y component of the position
+ */
+ public void drawImage(final org.eclipse.swt.graphics.Image image, final double x, final double y) {
+ final org.eclipse.swt.graphics.Rectangle bounds = image.getBounds();
+ TEMP_RECT.setRect(x, y, bounds.width, bounds.height);
+ SWTShapeManager.transform(TEMP_RECT, transform);
+ SWTShapeManager.awtToSWT(TEMP_RECT, SWT_RECT);
+
+ gc.drawImage(image, 0, 0, bounds.width, bounds.height, SWT_RECT.x, SWT_RECT.y, SWT_RECT.width, SWT_RECT.height);
+ }
+
+ /**
+ * Draws the source region from the image onto the destination region of the
+ * graphics context. Stretching if necessary.
+ *
+ * @param image image from which to copy
+ * @param srcX the left of the source region
+ * @param srcY the top of the source region
+ * @param srcW the width of the source region
+ * @param srcH the height of the source region
+ * @param destX the left of the destination region
+ * @param destY the top of the destination region
+ * @param destW the width of the destination region
+ * @param destH the height of the destination region
+ */
+ public void drawImage(final org.eclipse.swt.graphics.Image image, final int srcX, final int srcY, final int srcW,
+ final int srcH, final double destX, final double destY, final double destW, final double destH) {
+ TEMP_RECT.setRect(destX, destY, destW, destH);
+ SWTShapeManager.transform(TEMP_RECT, transform);
+ SWTShapeManager.awtToSWT(TEMP_RECT, SWT_RECT);
+
+ gc.drawImage(image, srcX, srcY, srcW, srcH, SWT_RECT.x, SWT_RECT.y, SWT_RECT.width, SWT_RECT.height);
+ }
+
+ /**
+ * Sets the line width to use when drawing shapes.
+ *
+ * @param lineWidth width of line when drawing shapes
+ */
+ public void setLineWidth(final double lineWidth) {
+ this.lineWidth = lineWidth;
+ }
+
+ /**
+ * Computes the width of the line after it passes through the current
+ * transform.
+ *
+ * @return resulting width of line after being transform
+ */
+ protected int getTransformedLineWidth() {
+ TEMP_LINE_RECT.setRect(0, 0, lineWidth, lineWidth);
+ SWTShapeManager.transform(TEMP_LINE_RECT, transform);
+
+ return (int) (Math.max(TEMP_LINE_RECT.getWidth(), 1) + 0.5);
+ }
+
+ /**
+ * Fills a gradient rectangle of in the direction specified.
+ *
+ * @param x left of resulting rectangle
+ * @param y top of resulting rectangle
+ * @param width width of resulting rectangle
+ * @param height height of resulting rectangle
+ * @param vertical whether the gradient should be drawn vertically or
+ * horizontally
+ */
+ public void fillGradientRectangle(final double x, final double y, final double width, final double height,
+ final boolean vertical) {
+ TEMP_RECT.setRect(x, y, width, height);
+ SWTShapeManager.transform(TEMP_RECT, transform);
+ SWTShapeManager.awtToSWT(TEMP_RECT, SWT_RECT);
+
+ gc.fillGradientRectangle(SWT_RECT.x, SWT_RECT.y, SWT_RECT.width, SWT_RECT.height, vertical);
+ }
+
+ /**
+ * Returns the advance width of the character provided in the current font.
+ *
+ * @param ch character to calculate the advance width of.
+ *
+ * @return advance width of the character in the current font
+ */
+ public int getAdvanceWidth(final char ch) {
+ final org.eclipse.swt.graphics.Font scaledFont = gc.getFont();
+ gc.setFont(curFont);
+ final int width = gc.getAdvanceWidth(ch);
+ gc.setFont(scaledFont);
+ return width;
+ }
+
+ /**
+ * Returns the width of the character provided in the current font.
+ *
+ * @param ch character to calculate the width of.
+ *
+ * @return width of the character in the current font
+ */
+ public int getCharWidth(final char ch) {
+ final org.eclipse.swt.graphics.Font scaledFont = gc.getFont();
+ gc.setFont(curFont);
+ final int width = gc.getCharWidth(ch);
+ gc.setFont(scaledFont);
+ return width;
+ }
+
+ /**
+ * Returns the extent of the provided string in the current font.
+ *
+ * @param str string to calculate the extent of.
+ *
+ * @return extent of the string in the current font
+ */
+ public org.eclipse.swt.graphics.Point stringExtent(final String str) {
+ final org.eclipse.swt.graphics.Font scaledFont = gc.getFont();
+ gc.setFont(curFont);
+ final org.eclipse.swt.graphics.Point extent = gc.stringExtent(str);
+ gc.setFont(scaledFont);
+ return extent;
+ }
+
+ /**
+ * Returns the extent of the provided text in the current font.
+ *
+ * @param str string to calculate the extent of.
+ *
+ * @return extent of the string in the current font
+ */
+ public org.eclipse.swt.graphics.Point textExtent(final String str) {
+ final org.eclipse.swt.graphics.Font scaledFont = gc.getFont();
+ gc.setFont(curFont);
+ final org.eclipse.swt.graphics.Point extent = gc.textExtent(str);
+ gc.setFont(scaledFont);
+ return extent;
+ }
+
+ /**
+ * Returns the extent of the provided text in the current font assuming the
+ * flags given.
+ *
+ * @param str string to calculate the extent of
+ * @param flags flags to apply to the rendered font before calculation of
+ * extent takes place
+ * @return extent of the string in the current font assuming flags provided
+ */
+ public org.eclipse.swt.graphics.Point textExtent(final String str, final int flags) {
+ final org.eclipse.swt.graphics.Font scaledFont = gc.getFont();
+ gc.setFont(curFont);
+ final org.eclipse.swt.graphics.Point extent = gc.textExtent(str, flags);
+ gc.setFont(scaledFont);
+ return extent;
+ }
+
+ // ///////////////////////////////
+ // CURRENTLY UNSUPPORTED METHODS
+ // ///////////////////////////////
+
+ /** {@inheritDoc} */
+ public void drawString(final AttributedCharacterIterator iterator, final int x, final int y) {
+ }
+
+ /** {@inheritDoc} */
+ public void drawString(final AttributedCharacterIterator iterator, final float x, final float y) {
+ }
+
+ /** {@inheritDoc} */
+ public void drawGlyphVector(final GlyphVector g, final float x, final float y) {
+ }
+
+ /**
+ * Returns whether the given rect and shape touch. If onStroke = true then
+ * it'll include the width of the stroke when calculating.
+ *
+ * @param rect rect to test
+ * @param s shape to test
+ * @param onStroke whether to consider the width of the stroke
+ * @return true if they touch
+ */
+ public boolean hit(final Rectangle rect, final Shape s, final boolean onStroke) {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ public void setComposite(final Composite comp) {
+ }
+
+ /** {@inheritDoc} */
+ public void setStroke(final Stroke s) {
+ }
+
+ /** {@inheritDoc} */
+ public void setRenderingHint(final Key hintKey, final Object hintValue) {
+ }
+
+ /** {@inheritDoc} */
+ public Object getRenderingHint(final Key hintKey) {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.awt.Graphics2D#setRenderingHints(Map)
+ */
+ public void setRenderingHints(final Map hints) {
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.awt.Graphics2D#addRenderingHints(Map)
+ */
+ public void addRenderingHints(final Map hints) {
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.awt.Graphics2D#getRenderingHints()
+ */
+ public RenderingHints getRenderingHints() {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.awt.Graphics2D#getComposite()
+ */
+ public Composite getComposite() {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.awt.Graphics2D#getStroke()
+ */
+ public Stroke getStroke() {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.awt.Graphics2D#getFontRenderContext()
+ */
+ public FontRenderContext getFontRenderContext() {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.awt.Graphics#create()
+ */
+ public Graphics create() {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.awt.Graphics#setPaintMode()
+ */
+ public void setPaintMode() {
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.awt.Graphics#setXORMode(Color)
+ */
+ public void setXORMode(final Color c1) {
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.awt.Graphics#getFontMetrics(Font)
+ */
+ public FontMetrics getFontMetrics(final Font f) {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.awt.Graphics2D#drawImage(Image, AffineTransform, ImageObserver)
+ */
+ public boolean drawImage(final Image img, final AffineTransform xform, final ImageObserver obs) {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.awt.Graphics2D#drawImage(BufferedImage, BufferedImageOp, int,
+ * int)
+ */
+ public void drawImage(final BufferedImage img, final BufferedImageOp op, final int x, final int y) {
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.awt.Graphics2D#drawRenderedImage(RenderedImage,
+ * AffineTransform)
+ */
+ public void drawRenderedImage(final RenderedImage img, final AffineTransform xform) {
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.awt.Graphics2D#drawRenderableImage(RenderableImage,
+ * AffineTransform)
+ */
+ public void drawRenderableImage(final RenderableImage img, final AffineTransform xform) {
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.awt.Graphics#drawImage(Image, int, int, ImageObserver)
+ */
+ public boolean drawImage(final Image img, final int x, final int y, final ImageObserver observer) {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.awt.Graphics#drawImage(Image, int, int, int, int,
+ * ImageObserver)
+ */
+ public boolean drawImage(final Image img, final int x, final int y, final int width, final int height,
+ final ImageObserver observer) {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.awt.Graphics#drawImage(Image, int, int, Color, ImageObserver)
+ */
+ public boolean drawImage(final Image img, final int x, final int y, final Color bgcolor,
+ final ImageObserver observer) {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.awt.Graphics#drawImage(Image, int, int, int, int, Color,
+ * ImageObserver)
+ */
+ public boolean drawImage(final Image img, final int x, final int y, final int width, final int height,
+ final Color bgcolor, final ImageObserver observer) {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.awt.Graphics#drawImage(Image, int, int, int, int, int, int,
+ * int, int, ImageObserver)
+ */
+ public boolean drawImage(final Image img, final int dx1, final int dy1, final int dx2, final int dy2,
+ final int sx1, final int sy1, final int sx2, final int sy2, final ImageObserver observer) {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.awt.Graphics#drawImage(Image, int, int, int, int, int, int,
+ * int, int, Color, ImageObserver)
+ */
+ public boolean drawImage(final Image img, final int dx1, final int dy1, final int dx2, final int dy2,
+ final int sx1, final int sy1, final int sx2, final int sy2, final Color bgcolor,
+ final ImageObserver observer) {
+ return false;
+ }
+
+ /**
+ * DO NOTHING - DISPOSED IN RENDERING CLASS.
+ */
+ public void dispose() {
+ }
+
+ // ///////////////////////////////
+ // CLEAN-UP METHODS
+ // ///////////////////////////////
+
+ /**
+ * Increases the number of uses of this graphics 2d object.
+ */
+ public static void incrementGCCount() {
+ CACHE_COUNT++;
+ }
+
+ /**
+ * Decreases the number of uses of this graphics 2d object.
+ */
+ public static void decrementGCCount() {
+ CACHE_COUNT--;
+
+ if (CACHE_COUNT == 0) {
+ for (final Iterator i = FONT_CACHE.values().iterator(); i.hasNext();) {
+ final org.eclipse.swt.graphics.Font font = (org.eclipse.swt.graphics.Font) i.next();
+ font.dispose();
+ }
+ for (final Iterator i = COLOR_CACHE.values().iterator(); i.hasNext();) {
+ final org.eclipse.swt.graphics.Color color = (org.eclipse.swt.graphics.Color) i.next();
+ color.dispose();
+ }
+ for (final Iterator i = SHAPE_CACHE.values().iterator(); i.hasNext();) {
+ final Path path = (Path) i.next();
+ path.dispose();
+ }
+ }
+ }
+
+}
diff --git a/swt/src/main/java/org/piccolo2d/extras/swt/SWTShapeManager.java b/swt/src/main/java/org/piccolo2d/extras/swt/SWTShapeManager.java
new file mode 100644
index 0000000..c3ce378
--- /dev/null
+++ b/swt/src/main/java/org/piccolo2d/extras/swt/SWTShapeManager.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2008-2010, 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.extras.swt;
+
+import java.awt.Shape;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.PathIterator;
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
+import java.util.ArrayList;
+
+import org.eclipse.swt.graphics.Rectangle;
+
+/**
+ * SWT shape manager.
+ *
+ * @author Lance Good
+ */
+public class SWTShapeManager {
+ private static AffineTransform IDENTITY_XFORM = new AffineTransform();
+ private static Point2D aPoint = new Point2D.Double();
+ private static ArrayList segList = new ArrayList();
+ private static double[] pts = new double[8];
+
+ /**
+ * Apply the specified transform to the specified rectangle, modifying the
+ * rect.
+ *
+ * @param rect The rectangle to be transformed
+ * @param at The transform to use to transform the rectangle
+ */
+ public static void transform(final Rectangle2D rect, final AffineTransform at) {
+ // First, transform all 4 corners of the rectangle
+ pts[0] = rect.getX(); // top left corner
+ pts[1] = rect.getY();
+ pts[2] = rect.getX() + rect.getWidth(); // top right corner
+ pts[3] = rect.getY();
+ pts[4] = rect.getX() + rect.getWidth(); // bottom right corner
+ pts[5] = rect.getY() + rect.getHeight();
+ pts[6] = rect.getX(); // bottom left corner
+ pts[7] = rect.getY() + rect.getHeight();
+ at.transform(pts, 0, pts, 0, 4);
+
+ // Then, find the bounds of those 4 transformed points.
+ double minX = pts[0];
+ double minY = pts[1];
+ double maxX = pts[0];
+ double maxY = pts[1];
+ int i;
+ for (i = 1; i < 4; i++) {
+ if (pts[2 * i] < minX) {
+ minX = pts[2 * i];
+ }
+ if (pts[2 * i + 1] < minY) {
+ minY = pts[2 * i + 1];
+ }
+ if (pts[2 * i] > maxX) {
+ maxX = pts[2 * i];
+ }
+ if (pts[2 * i + 1] > maxY) {
+ maxY = pts[2 * i + 1];
+ }
+ }
+ rect.setRect(minX, minY, maxX - minX, maxY - minY);
+ }
+
+ /**
+ * Populates the SWT rectangle with the provided Swing Rectangle2D's
+ * coordinates. Rounding up to the nearest integer.
+ *
+ * @param aRect awt rectangle to extract coordinates from
+ * @param sRect swt rectangle to populate
+ */
+ public static void awtToSWT(final Rectangle2D aRect, final Rectangle sRect) {
+ sRect.x = (int) (aRect.getX() + 0.5);
+ sRect.y = (int) (aRect.getY() + 0.5);
+ sRect.width = (int) (aRect.getWidth() + 0.5);
+ sRect.height = (int) (aRect.getHeight() + 0.5);
+ }
+
+ /**
+ * Converts the provided shape into an array of point coordinates given as
+ * one dimensional array with this format: x1,y1,x2,y3,....
+ *
+ * @param shape shape to convert
+ * @return point coordinates given as one dimensional array with this
+ * format: x1,y1,x2,y3,...
+ */
+ public static double[] shapeToPolyline(final Shape shape) {
+ segList.clear();
+ aPoint.setLocation(0, 0);
+
+ final PathIterator pi = shape.getPathIterator(IDENTITY_XFORM, 0.000000001);
+ while (!pi.isDone()) {
+ final int segType = pi.currentSegment(pts);
+ switch (segType) {
+ case PathIterator.SEG_MOVETO:
+ aPoint.setLocation(pts[0], pts[1]);
+ segList.add(new Point2D.Double(pts[0], pts[1]));
+ break;
+ case PathIterator.SEG_LINETO:
+ segList.add(new Point2D.Double(pts[0], pts[1]));
+ break;
+ case PathIterator.SEG_CLOSE:
+ segList.add(new Point2D.Double(aPoint.getX(), aPoint.getY()));
+ break;
+ default:
+ }
+ pi.next();
+ }
+
+ final double[] polyObj = new double[2 * segList.size()];
+ for (int i = 0; i < segList.size(); i++) {
+ final Point2D p2 = (Point2D) segList.get(i);
+ polyObj[2 * i] = (int) (p2.getX() + 0.5);
+ polyObj[2 * i + 1] = (int) (p2.getY() + 0.5);
+ }
+
+ return polyObj;
+ }
+
+ /**
+ * Transforms the given points by the transform provided, leaving the
+ * original points untouched.
+ *
+ * @param points points to transform
+ * @param at transform to apply
+ * @return transformed coordinates given in format x1,y2,x2,y2,...
+ */
+ public static int[] transform(final double[] points, final AffineTransform at) {
+ final int[] intPts = new int[points.length];
+ for (int i = 0; i < points.length / 2; i++) {
+ aPoint.setLocation(points[2 * i], points[2 * i + 1]);
+ at.transform(aPoint, aPoint);
+ intPts[2 * i] = (int) (aPoint.getX() + 0.5);
+ intPts[2 * i + 1] = (int) (aPoint.getY() + 0.5);
+ }
+ return intPts;
+ }
+}
diff --git a/swt/src/main/java/org/piccolo2d/extras/swt/SWTTimer.java b/swt/src/main/java/org/piccolo2d/extras/swt/SWTTimer.java
new file mode 100644
index 0000000..81ef80e
--- /dev/null
+++ b/swt/src/main/java/org/piccolo2d/extras/swt/SWTTimer.java
@@ -0,0 +1,331 @@
+/*
+ * Copyright (c) 2008-2010, 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.extras.swt;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.Timer;
+
+import org.eclipse.swt.widgets.Display;
+
+/**
+ * SWT timer.
+ *
+ * @author Lance Good
+ */
+public class SWTTimer extends Timer {
+ private static final long serialVersionUID = 1L;
+
+ private boolean notify = false;
+
+ private int initialDelay;
+ private int delay;
+ private boolean repeats = true;
+ private boolean coalesce = true;
+ private Runnable doPostEvent = null;
+ private Display display = null;
+
+ // These fields are maintained by TimerQueue.
+ // eventQueued can also be reset by the TimerQueue, but will only ever
+ // happen in applet case when TimerQueues thread is destroyed.
+ private long expirationTime;
+ private SWTTimer nextTimer;
+ boolean running;
+
+ /**
+ * DoPostEvent is a runnable class that fires actionEvents to the listeners
+ * on the EventDispatchThread, via invokeLater.
+ *
+ * @see #post
+ */
+ class SWTDoPostEvent implements Runnable {
+ public void run() {
+ if (notify) {
+ fireActionPerformed(new ActionEvent(SWTTimer.this, 0, null, System.currentTimeMillis(), 0));
+ if (coalesce) {
+ cancelEventOverride();
+ }
+ }
+ }
+
+ SWTTimer getTimer() {
+ return SWTTimer.this;
+ }
+ }
+
+ /**
+ * Constructor for SWTTimer.
+ *
+ * @param display display associated with this timer
+ * @param delay time in milliseconds between firings of this timer
+ * @param listener action listener to fire when the timer fires
+ */
+ public SWTTimer(final Display display, final int delay, final ActionListener listener) {
+ super(delay, listener);
+ this.delay = delay;
+ initialDelay = delay;
+
+ doPostEvent = new SWTDoPostEvent();
+ this.display = display;
+ }
+
+ /**
+ * Notifies all listeners that have registered interest for notification on
+ * this event type.
+ *
+ * @param e the action event to fire
+ */
+ protected void fireActionPerformed(final ActionEvent e) {
+ // Guaranteed to return a non-null array
+ final Object[] listeners = listenerList.getListenerList();
+
+ // Process the listeners last to first, notifying
+ // those that are interested in this event
+ for (int i = listeners.length - 2; i >= 0; i -= 2) {
+ if (listeners[i] == ActionListener.class) {
+ ((ActionListener) listeners[i + 1]).actionPerformed(e);
+ }
+ }
+ }
+
+ /**
+ * Returns the timer queue.
+ */
+ SWTTimerQueue timerQueue() {
+ return SWTTimerQueue.sharedInstance(display);
+ }
+
+ /**
+ * Sets the Timer
's delay, the number of milliseconds between
+ * successive action events.
+ *
+ * @param delay the delay in milliseconds
+ * @see #setInitialDelay
+ */
+ public void setDelay(final int delay) {
+ if (delay < 0) {
+ throw new IllegalArgumentException("Invalid delay: " + delay);
+ }
+ else {
+ this.delay = delay;
+ }
+ }
+
+ /**
+ * Returns the delay, in milliseconds, between firings of action events.
+ *
+ * @see #setDelay
+ * @see #getInitialDelay
+ * @return delay in milliseconds between firings of this timer
+ */
+ public int getDelay() {
+ return delay;
+ }
+
+ /**
+ * Sets the Timer
's initial delay, which by default is the same
+ * as the between-event delay. This is used only for the first action event.
+ * Subsequent action events are spaced using the delay property.
+ *
+ * @param initialDelay the delay, in milliseconds, between the invocation of
+ * the start
method and the first action event fired
+ * by this timer
+ *
+ * @see #setDelay
+ */
+ public void setInitialDelay(final int initialDelay) {
+ if (initialDelay < 0) {
+ throw new IllegalArgumentException("Invalid initial delay: " + initialDelay);
+ }
+ else {
+ this.initialDelay = initialDelay;
+ }
+ }
+
+ /**
+ * Returns the Timer
's initial delay. By default this is the
+ * same as the value returned by getDelay.
+ *
+ * @see #setInitialDelay
+ * @see #setDelay
+ * @return the initial delay of this timer
+ */
+ public int getInitialDelay() {
+ return initialDelay;
+ }
+
+ /**
+ * If flag
is false
, instructs the
+ * Timer
to send only one action event to its listeners.
+ *
+ * @param flag specify false
to make the timer stop after
+ * sending its first action event
+ */
+ public void setRepeats(final boolean flag) {
+ repeats = flag;
+ }
+
+ /**
+ * Returns true
(the default) if the Timer
will
+ * send an action event to its listeners multiple times.
+ *
+ * @see #setRepeats
+ * @return true if this timer should repeat when completed
+ */
+ public boolean isRepeats() {
+ return repeats;
+ }
+
+ /**
+ * Sets whether the Timer
coalesces multiple pending
+ * ActionEvent
firings. A busy application may not be able to
+ * keep up with a Timer
's event generation, causing multiple
+ * action events to be queued. When processed, the application sends these
+ * events one after the other, causing the Timer
's listeners to
+ * receive a sequence of events with no delay between them. Coalescing
+ * avoids this situation by reducing multiple pending events to a single
+ * event. Timer
s coalesce events by default.
+ *
+ * @param flag specify false
to turn off coalescing
+ */
+ public void setCoalesce(final boolean flag) {
+ final boolean old = coalesce;
+ coalesce = flag;
+ if (!old && coalesce) {
+ // We must do this as otherwise if the Timer once notified
+ // in !coalese mode notify will be stuck to true and never
+ // become false.
+ cancelEventOverride();
+ }
+ }
+
+ /**
+ * Returns true
if the Timer
coalesces multiple
+ * pending action events.
+ *
+ * @see #setCoalesce
+ * @return true if this timer coalesces multiple pending action events
+ */
+ public boolean isCoalesce() {
+ return coalesce;
+ }
+
+ /**
+ * Starts the Timer
, causing it to start sending action events
+ * to its listeners.
+ *
+ * @see #stop
+ */
+ public void start() {
+ timerQueue().addTimer(this, System.currentTimeMillis() + getInitialDelay());
+ }
+
+ /**
+ * Returns true
if the Timer
is running.
+ *
+ * @see #start
+ * @return true if this timer is scheduled to run
+ */
+ public boolean isRunning() {
+ return timerQueue().containsTimer(this);
+ }
+
+ /**
+ * Stops the Timer
, causing it to stop sending action events to
+ * its listeners.
+ *
+ * @see #start
+ */
+ public void stop() {
+ timerQueue().removeTimer(this);
+ cancelEventOverride();
+ }
+
+ /**
+ * Restarts the Timer
, canceling any pending firings and
+ * causing it to fire with its initial delay.
+ */
+ public void restart() {
+ stop();
+ start();
+ }
+
+ /**
+ * Resets the internal state to indicate this Timer shouldn't notify any of
+ * its listeners. This does not stop a repeatable Timer from firing again,
+ * use stop
for that.
+ */
+ synchronized void cancelEventOverride() {
+ notify = false;
+ }
+
+ synchronized void postOverride() {
+ if (!notify || !coalesce) {
+ notify = true;
+ display.asyncExec(doPostEvent);
+ }
+ }
+
+ /**
+ * @param expirationTime the expirationTime to set
+ */
+ public void setExpirationTime(final long expirationTime) {
+ this.expirationTime = expirationTime;
+ }
+
+ /**
+ * @return the expirationTime
+ */
+ public long getExpirationTime() {
+ return expirationTime;
+ }
+
+ /**
+ * @param nextTimer the nextTimer to set
+ */
+ void setNextTimer(final SWTTimer nextTimer) {
+ this.nextTimer = nextTimer;
+ }
+
+ /**
+ * @return the nextTimer
+ */
+ SWTTimer getNextTimer() {
+ return nextTimer;
+ }
+
+ /**
+ * @param running the running to set
+ */
+ public void setRunning(final boolean running) {
+ this.running = running;
+ }
+
+}
diff --git a/swt/src/main/java/org/piccolo2d/extras/swt/SWTTimerQueue.java b/swt/src/main/java/org/piccolo2d/extras/swt/SWTTimerQueue.java
new file mode 100644
index 0000000..f73479a
--- /dev/null
+++ b/swt/src/main/java/org/piccolo2d/extras/swt/SWTTimerQueue.java
@@ -0,0 +1,381 @@
+/*
+ * Copyright (c) 2008-2010, 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.extras.swt;
+
+import org.eclipse.swt.widgets.Display;
+
+/**
+ * The SWTTimerQueue is a queue of timers. It has been implemented as a linked
+ * list of SWTTimer objects.
+ *
+ * @author Lance Good
+ */
+public class SWTTimerQueue implements Runnable {
+ private static SWTTimerQueue instance;
+
+ private final Display display;
+
+ private SWTTimer firstTimer;
+ private boolean running;
+
+ /**
+ * Creates a timer queue that will be attached the the provided display.
+ * It's Timers are expected to modify only this display, or none.
+ *
+ * @param display the display that will get updated by this queue's timers.
+ */
+ public SWTTimerQueue(final Display display) {
+ this.display = display;
+
+ // Now start the TimerQueue thread.
+ start();
+ }
+
+ /**
+ * Returns the singleton instance of the SWTTimerQueue. Take note that even
+ * when called with different displays it will always return the same result
+ * as the first call.
+ *
+ * @param display display to associate with this Timer Queue's Activities
+ * @return singleton instance of SWTTimerQueue
+ */
+ public static SWTTimerQueue sharedInstance(final Display display) {
+ if (instance == null) {
+ instance = new SWTTimerQueue(display);
+ }
+ return instance;
+ }
+
+ /**
+ * Starts the timer queue. If it is already running, a RuntimeException will
+ * be thrown.
+ */
+ synchronized void start() {
+ if (running) {
+ throw new RuntimeException("Can't start a TimerQueue that is already running");
+ }
+
+ // Ensures that the Thread will be started from the display thread.
+ Display.getDefault().asyncExec(new Runnable() {
+ public void run() {
+ final Thread timerThread = new Thread(SWTTimerQueue.this, "TimerQueue");
+ timerThread.setDaemon(true);
+ timerThread.setPriority(Thread.NORM_PRIORITY);
+ timerThread.start();
+ }
+ });
+
+ running = true;
+ }
+
+ /**
+ * Stops the TimerQueue Thread.
+ */
+ synchronized void stop() {
+ running = false;
+ notifyAll();
+ }
+
+ /**
+ * Adds the provided timer to the queue of scheduled timers.
+ *
+ * @param timer timer to add
+ * @param expirationTime time at which the timer is to be stopped and
+ * removed from the queue. Given in unix time.
+ */
+ synchronized void addTimer(final SWTTimer timer, final long expirationTime) {
+ // If the Timer is already in the queue, then do nothing
+ if (!timer.isRunning()) {
+ insertTimer(timer, expirationTime);
+
+ timer.setExpirationTime(expirationTime);
+
+ timer.setRunning(true);
+ notifyAll();
+ }
+ }
+
+ /**
+ * Insert the Timer into the queue in the order they will expire. If
+ * multiple timers are set to expire at the same time, it will insert it
+ * after the last one; that way they expire in the order they came in.
+ *
+ * @param timer timer to insert into the queue
+ * @param expirationTime time in UNIX time at which the new timer should
+ * expire
+ */
+ private void insertTimer(final SWTTimer timer, final long expirationTime) {
+ SWTTimer previousTimer = findLastTimerExpiringBefore(expirationTime);
+ if (previousTimer == null) {
+ firstTimer = timer;
+ }
+ else {
+ timer.setNextTimer(previousTimer.getNextTimer());
+ previousTimer.setNextTimer(timer);
+ }
+ }
+
+ /**
+ * Finds the last timer that will expire before or at the given expiration
+ * time. If there are multiple timers expiring at the same time, the last
+ * one in the queue will be returned.
+ *
+ * @param expirationTime expiration to compare against timers in the queue
+ * @return last timer that will expire before or at the given expiration
+ * time
+ */
+ private SWTTimer findLastTimerExpiringBefore(final long expirationTime) {
+ SWTTimer previousTimer = null;
+ SWTTimer nextTimer = firstTimer;
+
+ while (nextTimer != null && nextTimer.getExpirationTime() > expirationTime) {
+ previousTimer = nextTimer;
+ nextTimer = nextTimer.getNextTimer();
+ }
+
+ return previousTimer;
+
+ }
+
+ /**
+ * Removes the provided timer from the Timer Queue. If it is not found, then
+ * nothing happens.
+ *
+ * @param timer timer to remove from the queue
+ */
+ synchronized void removeTimer(final SWTTimer timer) {
+ if (!timer.isRunning()) {
+ return;
+ }
+
+ if (timer == firstTimer) {
+ firstTimer = timer.getNextTimer();
+ }
+ else {
+ SWTTimer previousTimer = findLastTimerBefore(timer);
+ if (previousTimer != null) {
+ previousTimer.setNextTimer(timer.getNextTimer());
+ }
+ }
+
+ timer.setExpirationTime(0);
+ timer.setNextTimer(null);
+ timer.setRunning(false);
+ }
+
+ /**
+ * Finds the timer that immediately precedes the provided timer in the
+ * queue.
+ *
+ * @param timer to search for
+ * @return timer immediately preceding found timer, or null if not found
+ */
+ private SWTTimer findLastTimerBefore(final SWTTimer timer) {
+ SWTTimer previousTimer = null;
+ SWTTimer currentTimer = firstTimer;
+
+ while (currentTimer != null) {
+ if (currentTimer == timer) {
+ return previousTimer;
+ }
+
+ previousTimer = currentTimer;
+ currentTimer = currentTimer.getNextTimer();
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns true if this timer queue contains the given timer.
+ *
+ * @param timer timer being checked
+ * @return true if timer is scheduled in this queue
+ */
+ synchronized boolean containsTimer(final SWTTimer timer) {
+ // TODO: making this use isRunning without causing an infinite loop
+ return timer.running;
+ }
+
+ /**
+ * If there are a ton of timers, this method may never return. It loops
+ * checking to see if the head of the Timer list has expired. If it has, it
+ * posts the Timer and reschedules it if necessary.
+ *
+ * @return how long the app can take before it should invoke this method
+ * again.
+ */
+ private synchronized long postExpiredTimers() {
+ SWTTimer timer;
+ long currentTime;
+ long timeToWait;
+
+ // The timeToWait we return should never be negative and only be zero
+ // when we have no Timers to wait for.
+
+ do {
+ timer = firstTimer;
+ if (timer == null) {
+ return 0;
+ }
+
+ currentTime = System.currentTimeMillis();
+ timeToWait = timer.getExpirationTime() - currentTime;
+
+ if (timeToWait <= 0) {
+ try {
+ timer.postOverride(); // have timer post an event
+ }
+ catch (final SecurityException e) {
+ throw new RuntimeException("Could not post event", e);
+ }
+
+ // Remove the timer from the queue
+ removeTimer(timer);
+
+ // This tries to keep the interval uniform at
+ // the cost of drift.
+ if (timer.isRepeats()) {
+ addTimer(timer, currentTime + timer.getDelay());
+ }
+
+ // Allow other threads to call addTimer() and removeTimer()
+ // even when we are posting Timers like mad. Since the wait()
+ // releases the lock, be sure not to maintain any state
+ // between iterations of the loop.
+
+ try {
+ wait(1);
+ }
+ catch (final InterruptedException e) {
+ // Nothing to do
+ }
+ }
+ } while (timeToWait <= 0);
+
+ return timeToWait;
+ }
+
+ /**
+ * Dispatches work to timers until the queue is told to stop running.
+ */
+ public synchronized void run() {
+ long timeToWait;
+
+ try {
+ while (running) {
+ timeToWait = postExpiredTimers();
+ try {
+ wait(timeToWait);
+ }
+ catch (final InterruptedException e) {
+ // Nothing to do
+ }
+ }
+ }
+ catch (final ThreadDeath td) {
+ running = false;
+ // Mark all the timers we contain as not being queued.
+ SWTTimer timer = firstTimer;
+ while (timer != null) {
+ timer.cancelEventOverride();
+ timer = timer.getNextTimer();
+ }
+ display.asyncExec(new SWTTimerQueueRestart(display));
+ throw td;
+ }
+ }
+
+ /**
+ * Generates a string handy for debugging the contents of the timer queue.
+ *
+ * @return String representation of the queue for use in debugging
+ */
+ public synchronized String toString() {
+ StringBuffer buf;
+ SWTTimer nextTimer;
+
+ buf = new StringBuffer();
+ buf.append("TimerQueue (");
+
+ nextTimer = firstTimer;
+ while (nextTimer != null) {
+ buf.append(nextTimer.toString());
+
+ nextTimer = nextTimer.getNextTimer();
+ if (nextTimer != null) {
+ buf.append(", ");
+ }
+ }
+
+ buf.append(")");
+ return buf.toString();
+ }
+
+ /**
+ * Runnable that will message the shared instance of the Timer Queue to
+ * restart.
+ */
+ protected static class SWTTimerQueueRestart implements Runnable {
+ /** Tracks whether a restart has been attempted. */
+ private boolean attemptedStart;
+
+ private final Display display;
+
+ /**
+ * Constructs a QueueRestart Runnable that will message the Timer Queue
+ * to Restart.
+ *
+ * @param display display associated with the SWTTimerQueue
+ */
+ public SWTTimerQueueRestart(final Display display) {
+ this.display = display;
+ }
+
+ /**
+ * Attempts to restart the queue associated with the display.
+ */
+ public synchronized void run() {
+ if (attemptedStart) {
+ return;
+ }
+
+ final SWTTimerQueue q = SWTTimerQueue.sharedInstance(display);
+
+ synchronized (q) {
+ if (!q.running) {
+ q.start();
+ }
+ }
+
+ attemptedStart = true;
+ }
+ }
+
+}
diff --git a/swt/src/main/java/org/piccolo2d/extras/swt/package.html b/swt/src/main/java/org/piccolo2d/extras/swt/package.html
new file mode 100644
index 0000000..1af36f5
--- /dev/null
+++ b/swt/src/main/java/org/piccolo2d/extras/swt/package.html
@@ -0,0 +1,34 @@
+
+
+
This package provides a SWT implementation of the core Piccolo library.
+ + diff --git a/swt/src/test/java/edu/umd/cs/piccolox/swt/PSWTBoundsHandleTest.java b/swt/src/test/java/edu/umd/cs/piccolox/swt/PSWTBoundsHandleTest.java deleted file mode 100644 index bd7b266..0000000 --- a/swt/src/test/java/edu/umd/cs/piccolox/swt/PSWTBoundsHandleTest.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.swt; - -import org.piccolo2d.PCamera; -import org.piccolo2d.PNode; -import org.piccolo2d.event.PInputEventListener; -import org.piccolo2d.extras.util.PBoundsLocator; - -import junit.framework.TestCase; - - - - -/** - * Unit test for PSWTBoundsHandle. - */ -public class PSWTBoundsHandleTest extends TestCase { - private PNode node; - - public void setUp() { - node = new PNode(); - node.setBounds(0, 0, 100, 100); - } - - public void testAddBoundsHandlesToNodeAddsHandles() { - PSWTBoundsHandle.addBoundsHandlesTo(node); - assertEquals(8, node.getChildrenCount()); - - for (int i = 0; i < 8; i++) { - PNode child = node.getChild(i); - assertTrue(child instanceof PSWTBoundsHandle); - } - } - - public void testAddStickyBoundsHandlesToNodeAddsHandles() { - PCamera camera = new PCamera(); - PSWTBoundsHandle.addStickyBoundsHandlesTo(node, camera); - assertEquals(0, node.getChildrenCount()); - assertEquals(8, camera.getChildrenCount()); - - for (int i = 0; i < 8; i++) { - PNode child = camera.getChild(i); - assertTrue(child instanceof PSWTBoundsHandle); - } - } - - public void testRemoveBoundsHandlesRemovesOnlyHandles() { - PNode child = new PNode(); - node.addChild(child); - PSWTBoundsHandle.addBoundsHandlesTo(node); - PSWTBoundsHandle.removeBoundsHandlesFrom(node); - assertEquals(1, node.getChildrenCount()); - assertEquals(child, node.getChild(0)); - } - - public void testRemoveBoundsHandlesDoesNothingWhenNoHandles() { - PNode child = new PNode(); - node.addChild(child); - PSWTBoundsHandle.removeBoundsHandlesFrom(node); - assertEquals(1, node.getChildrenCount()); - } - - public void testCursorHandlerIsInstalledByDefault() { - PSWTBoundsHandle handle = new PSWTBoundsHandle(PBoundsLocator.createEastLocator(node)); - PInputEventListener dragHandler = handle.getHandleDraggerHandler(); - PInputEventListener cursorHandler = handle.getHandleCursorEventHandler(); - assertNotNull(cursorHandler); - PInputEventListener[] listeners = handle.getInputEventListeners(); - assertEquals(2, listeners.length); - assertTrue(cursorHandler == listeners[0] || cursorHandler == listeners[1]); - assertTrue(dragHandler == listeners[0] || dragHandler == listeners[1]); - } -} diff --git a/swt/src/test/java/edu/umd/cs/piccolox/swt/PSWTCanvasTest.java b/swt/src/test/java/edu/umd/cs/piccolox/swt/PSWTCanvasTest.java deleted file mode 100644 index e27f8ac..0000000 --- a/swt/src/test/java/edu/umd/cs/piccolox/swt/PSWTCanvasTest.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.swt; - -import junit.framework.TestCase; - -import org.eclipse.swt.layout.FillLayout; - -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Shell; -import org.piccolo2d.event.PInputEventListener; -import org.piccolo2d.event.PPanEventHandler; -import org.piccolo2d.event.PZoomEventHandler; - - -/** - * Unit test for PSWTCanvas. - */ -public class PSWTCanvasTest extends TestCase { - private PSWTCanvas canvas; - - public void setUp() { - final Shell shell = new Shell(Display.getDefault()); - shell.setLayout(new FillLayout()); - canvas = new PSWTCanvas(shell, 0); - } - - public void testPanEventListenerIsInstalledByDefault() { - PPanEventHandler handler = canvas.getPanEventHandler(); - assertNotNull(handler); - - int handlerIndex = getHandlerIndex(handler); - assertFalse("Pan Event Handler not installed", handlerIndex == -1); - } - - public void testZoomEventListenerIsInstalledByDefault() { - PZoomEventHandler handler = canvas.getZoomEventHandler(); - assertNotNull(handler); - - int handlerIndex = getHandlerIndex(handler); - assertFalse("Zoom Event Handler not installed", handlerIndex == -1); - } - - private int getHandlerIndex(PInputEventListener handler) { - PInputEventListener[] listeners = canvas.getCamera().getInputEventListeners(); - int handlerIndex = -1; - for (int i = 0; i < listeners.length; i++) { - if (listeners[i] == handler) { - handlerIndex = i; - } - } - return handlerIndex; - } - - public void testAnimatingDefaultsToFalse() { - assertFalse(canvas.getAnimating()); - } - - public void testInteractingDefaultsToFalse() { - assertFalse(canvas.getInteracting()); - } - - public void testInteractingWorksByCountingCallsToSetInteracting() { - canvas.setInteracting(true); - assertTrue(canvas.getInteracting()); - - canvas.setInteracting(true); - assertTrue(canvas.getInteracting()); - - canvas.setInteracting(false); - // This is terrible - assertTrue(canvas.getInteracting()); - - canvas.setInteracting(false); - assertFalse(canvas.getInteracting()); - } - - public void testCanvasIsDoubleBufferedByDefault() { - assertTrue(canvas.getDoubleBuffered()); - } - - public void testDoubleBufferingPersists() { - canvas.setDoubleBuffered(false); - assertFalse(canvas.getDoubleBuffered()); - canvas.setDoubleBuffered(true); - assertTrue(canvas.getDoubleBuffered()); - } -} diff --git a/swt/src/test/java/edu/umd/cs/piccolox/swt/PSWTHandleTest.java b/swt/src/test/java/edu/umd/cs/piccolox/swt/PSWTHandleTest.java deleted file mode 100644 index 4c5333f..0000000 --- a/swt/src/test/java/edu/umd/cs/piccolox/swt/PSWTHandleTest.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.swt; - -import org.piccolo2d.PNode; -import org.piccolo2d.event.PInputEventListener; -import org.piccolo2d.extras.util.PBoundsLocator; -import org.piccolo2d.extras.util.PLocator; - -import junit.framework.TestCase; - - - - -/** - * Unit test for PSWTHandle. - */ -public class PSWTHandleTest extends TestCase { - private PNode node; - private PSWTHandle handle; - private PBoundsLocator locator; - - public void setUp() throws Exception { - node = new PNode(); - locator = PBoundsLocator.createEastLocator(node); - handle = new PSWTHandle(locator); - node.setBounds(0, 0, 100, 100); - node.addChild(handle); - } - - public void testDefaultsAreCorrect() { - assertEquals(PSWTHandle.DEFAULT_COLOR, handle.getPaint()); - assertEquals(PSWTHandle.DEFAULT_HANDLE_SIZE + 2 /** for border pen */ - , handle.getHeight(), Float.MIN_VALUE); - } - - public void testLocatorPersists() { - assertSame(locator, handle.getLocator()); - - PLocator newLocator = PBoundsLocator.createWestLocator(node); - handle.setLocator(newLocator); - assertSame(newLocator, handle.getLocator()); - } - - public void testHandleHasDragHandlerInstalled() { - PInputEventListener dragHandler = handle.getHandleDraggerHandler(); - assertNotNull(dragHandler); - - PInputEventListener[] installedListeners = handle.getInputEventListeners(); - assertEquals(1, installedListeners.length); - assertSame(dragHandler, installedListeners[0]); - } - - public void testChangingParentDoesNotChangeLocatorNode() { - handle.relocateHandle(); - PNode newParent = new PNode(); - newParent.setBounds(50, 50, 100, 100); - - final double originalX = handle.getX(); - handle.setParent(newParent); - - final double newX = handle.getX(); - - assertEquals(newX, originalX, Double.MIN_VALUE); - } -} diff --git a/swt/src/test/java/edu/umd/cs/piccolox/swt/PSWTImageTest.java b/swt/src/test/java/edu/umd/cs/piccolox/swt/PSWTImageTest.java deleted file mode 100644 index b4698f6..0000000 --- a/swt/src/test/java/edu/umd/cs/piccolox/swt/PSWTImageTest.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.swt; - -import java.io.File; - -import junit.framework.TestCase; - -import org.eclipse.swt.graphics.Image; -import org.eclipse.swt.graphics.Rectangle; - -import org.eclipse.swt.layout.FillLayout; - -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Shell; - -/** - * Unit test for PSWTImage. - */ -public class PSWTImageTest extends TestCase { - File imageFile; - PSWTCanvas canvas; - PSWTImage imageNode; - Image image; - - public void setUp() throws Exception { - final Display display = Display.getDefault(); - final Shell shell = new Shell(display); - shell.setLayout(new FillLayout()); - canvas = new PSWTCanvas(shell, 0); - imageNode = new PSWTImage(canvas); - image = new Image(display, new Rectangle(0, 0, 100, 100)); - } - - public void testImageShouldDefaultToNull() { - assertNull(imageNode.getImage()); - } - - public void testPaintShouldDoNothingWhenImageIsNull() { - // if it tries to use the graphics context, it would throw a NPE - imageNode.paint(null); - } - - public void testImageInConstructorPersists() { - imageNode = new PSWTImage(canvas, image); - assertSame(image, imageNode.getImage()); - } - - public void testDisposingCanvasDisposesImage() { - final boolean[] called = new boolean[1]; - called[0] = false; - imageNode = new PSWTImage(canvas, image) { - protected void disposeImage() { - called[0] = true; - super.disposeImage(); - } - }; - canvas.dispose(); - assertTrue(called[0]); - } -} diff --git a/swt/src/test/java/edu/umd/cs/piccolox/swt/PSWTPathTest.java b/swt/src/test/java/edu/umd/cs/piccolox/swt/PSWTPathTest.java deleted file mode 100755 index 3d60525..0000000 --- a/swt/src/test/java/edu/umd/cs/piccolox/swt/PSWTPathTest.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.swt; - -import java.awt.geom.Point2D; - -import org.piccolo2d.util.PBounds; - -import junit.framework.TestCase; - - -/** - * Unit test for PSWTPath. - */ -public class PSWTPathTest extends TestCase { - - public void testCenterEmpty() { - PSWTPath path = new PSWTPath(); - - PBounds bounds = path.getBoundsReference(); - assertEquals(0.0d, bounds.getX(), 0.1d); - assertEquals(0.0d, bounds.getY(), 0.1d); - assertEquals(0.0d, bounds.getHeight(), 0.1d); - assertEquals(0.0d, bounds.getWidth(), 0.1d); - - Point2D center = path.getCenter(); - assertEquals(0.0d, center.getX(), 0.1d); - assertEquals(0.0d, center.getY(), 0.1d); - } - - public void testCenter() { - PSWTPath path = PSWTPath.createRectangle(10.0f, 20.0f, 100.0f, 200.0f); - - PBounds bounds = path.getBoundsReference(); - // hard to believe the tolerance in SWT is this poor - assertEquals(10.0d, bounds.getX(), 1.0d); - assertEquals(20.0d, bounds.getY(), 1.0d); - assertEquals(200.0d, bounds.getHeight(), 2.0d); - assertEquals(100.0d, bounds.getWidth(), 2.0d); - - Point2D center = path.getCenter(); - assertEquals(60.0d, center.getX(), 0.1d); - assertEquals(120.0d, center.getY(), 0.1d); - } - - public void testCenterScale() { - PSWTPath path = PSWTPath.createRectangle(10.0f, 20.0f, 100.0f, 200.0f); - path.scale(10.0d); - - PBounds bounds = path.getBoundsReference(); - // hard to believe the tolerance in SWT is this poor - assertEquals(10.0d, bounds.getX(), 1.0d); - assertEquals(20.0d, bounds.getY(), 1.0d); - assertEquals(200.0d, bounds.getHeight(), 2.0d); - assertEquals(100.0d, bounds.getWidth(), 2.0d); - - // center is calculated in terms of local bounds, not full bounds - Point2D center = path.getCenter(); - assertEquals(60.0d, center.getX(), 0.1d); - assertEquals(120.0d, center.getY(), 0.1d); - } - - public void testCenterRotate() { - PSWTPath path = PSWTPath.createRectangle(10.0f, 20.0f, 100.0f, 200.0f); - path.rotate(Math.PI / 8.0d); - - PBounds bounds = path.getBoundsReference(); - // hard to believe the tolerance in SWT is this poor - assertEquals(10.0d, bounds.getX(), 1.0d); - assertEquals(20.0d, bounds.getY(), 1.0d); - assertEquals(200.0d, bounds.getHeight(), 2.0d); - assertEquals(100.0d, bounds.getWidth(), 2.0d); - - // center is calculated in terms of local bounds, not full bounds - Point2D center = path.getCenter(); - assertEquals(60.0d, center.getX(), 0.1d); - assertEquals(120.0d, center.getY(), 0.1d); - } -} diff --git a/swt/src/test/java/edu/umd/cs/piccolox/swt/PSWTTextTest.java b/swt/src/test/java/edu/umd/cs/piccolox/swt/PSWTTextTest.java deleted file mode 100644 index a85374a..0000000 --- a/swt/src/test/java/edu/umd/cs/piccolox/swt/PSWTTextTest.java +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.swt; - -import java.awt.Color; -import java.awt.Font; - -import java.awt.geom.Point2D; - -import org.piccolo2d.util.PBounds; - -import junit.framework.TestCase; - - -/** - * Unit test for PSWTText. - */ -public class PSWTTextTest extends TestCase { - private PSWTText textNode; - - public void setUp() { - textNode = new PSWTText(); - } - - public void testConstructorRemembersTextValue() { - textNode = new PSWTText("Hello World\n\n"); - assertEquals("Hello World\n\n", textNode.getText()); - } - - public void testTextPersistsTrainingAndInternalNewlines() { - textNode.setText("Hello\nWorld\n\n"); - assertEquals("Hello\nWorld\n\n", textNode.getText()); - } - - public void testDefaultPropertiesAreCorrect() { - assertEquals(Color.BLACK, textNode.getPenColor()); - assertEquals(Color.BLACK, textNode.getPenPaint()); - assertNull(textNode.getBackgroundColor()); - assertNull(textNode.getPaint()); - assertEquals(5.5, textNode.getGreekThreshold(), Double.MIN_VALUE); - assertFalse(textNode.isTransparent()); - } - - public void testDefaultFontIsCorrect() { - Font font = textNode.getFont(); - assertNotNull(font); - assertFalse(font.isBold()); - assertEquals(12, font.getSize()); - } - - public void testPenColorPersists() { - textNode.setPenColor(Color.RED); - assertEquals(Color.RED, textNode.getPenColor()); - } - - public void testPenPaintPersists() { - textNode.setPenPaint(Color.RED); - assertEquals(Color.RED, textNode.getPenPaint()); - } - - public void testTransparencyPersists() { - textNode.setTransparent(true); - assertTrue(textNode.isTransparent()); - } - - public void testBackgroundColor() { - textNode.setBackgroundColor(Color.RED); - assertEquals(Color.RED, textNode.getBackgroundColor()); - } - - public void testPenPaintAndPenColorAreSameThing() { - textNode.setPenColor(Color.RED); - assertEquals(Color.RED, textNode.getPenPaint()); - - textNode.setPenPaint(Color.BLUE); - assertEquals(Color.BLUE, textNode.getPenColor()); - } - - public void testBackgroundColorAndPaintAreSameThing() { - textNode.setBackgroundColor(Color.RED); - assertEquals(Color.RED, textNode.getPaint()); - - textNode.setPaint(Color.BLUE); - assertEquals(Color.BLUE, textNode.getBackgroundColor()); - } - - public void testGreekThresholdPersists() { - textNode.setGreekThreshold(0.1); - assertEquals(0.1, textNode.getGreekThreshold(), Double.MIN_VALUE); - } - - public void testShrinkingFontShrinksBounds() { - textNode.setText("Hello\nWorld"); - - PBounds startBounds = textNode.getBounds(); - Font startFont = textNode.getFont(); - Font newFont = new Font(startFont.getFontName(), startFont.getStyle(), 8); - - textNode.setFont(newFont); - assertSame(newFont, textNode.getFont()); - - PBounds endBounds = textNode.getBounds(); - assertTrue(startBounds.width > endBounds.width); - assertTrue(startBounds.height > endBounds.height); - } - - public void testTranslationsBehaveLogically() { - textNode.setTranslation(1, 2); - assertEquals(1, textNode.getTranslateX(), Double.MIN_VALUE); - assertEquals(2, textNode.getTranslateY(), Double.MIN_VALUE); - - textNode.setTranslateX(3); - assertEquals(3, textNode.getTranslateX(), Double.MIN_VALUE); - - textNode.setTranslateY(4); - assertEquals(4, textNode.getTranslateY(), Double.MIN_VALUE); - - assertEquals(new Point2D.Double(3, 4), textNode.getTranslation()); - - textNode.setTranslation(new Point2D.Double(5, 6)); - assertEquals(new Point2D.Double(5, 6), textNode.getTranslation()); - } - - public void testTranslatingDoesntAffectSize() { - textNode.setText("Hello"); - PBounds startBounds = textNode.getBounds(); - textNode.translate(1, 2); - PBounds endBounds = textNode.getBounds(); - assertEquals(startBounds.width, endBounds.width, Double.MIN_VALUE); - assertEquals(startBounds.height, endBounds.height, Double.MIN_VALUE); - } - -} diff --git a/swt/src/test/java/org/piccolo2d/extras/swt/PSWTBoundsHandleTest.java b/swt/src/test/java/org/piccolo2d/extras/swt/PSWTBoundsHandleTest.java new file mode 100644 index 0000000..2b28300 --- /dev/null +++ b/swt/src/test/java/org/piccolo2d/extras/swt/PSWTBoundsHandleTest.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2008-2010, 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.extras.swt; + +import org.piccolo2d.PCamera; +import org.piccolo2d.PNode; +import org.piccolo2d.event.PInputEventListener; +import org.piccolo2d.extras.swt.PSWTBoundsHandle; +import org.piccolo2d.extras.util.PBoundsLocator; + +import junit.framework.TestCase; + + + + +/** + * Unit test for PSWTBoundsHandle. + */ +public class PSWTBoundsHandleTest extends TestCase { + private PNode node; + + public void setUp() { + node = new PNode(); + node.setBounds(0, 0, 100, 100); + } + + public void testAddBoundsHandlesToNodeAddsHandles() { + PSWTBoundsHandle.addBoundsHandlesTo(node); + assertEquals(8, node.getChildrenCount()); + + for (int i = 0; i < 8; i++) { + PNode child = node.getChild(i); + assertTrue(child instanceof PSWTBoundsHandle); + } + } + + public void testAddStickyBoundsHandlesToNodeAddsHandles() { + PCamera camera = new PCamera(); + PSWTBoundsHandle.addStickyBoundsHandlesTo(node, camera); + assertEquals(0, node.getChildrenCount()); + assertEquals(8, camera.getChildrenCount()); + + for (int i = 0; i < 8; i++) { + PNode child = camera.getChild(i); + assertTrue(child instanceof PSWTBoundsHandle); + } + } + + public void testRemoveBoundsHandlesRemovesOnlyHandles() { + PNode child = new PNode(); + node.addChild(child); + PSWTBoundsHandle.addBoundsHandlesTo(node); + PSWTBoundsHandle.removeBoundsHandlesFrom(node); + assertEquals(1, node.getChildrenCount()); + assertEquals(child, node.getChild(0)); + } + + public void testRemoveBoundsHandlesDoesNothingWhenNoHandles() { + PNode child = new PNode(); + node.addChild(child); + PSWTBoundsHandle.removeBoundsHandlesFrom(node); + assertEquals(1, node.getChildrenCount()); + } + + public void testCursorHandlerIsInstalledByDefault() { + PSWTBoundsHandle handle = new PSWTBoundsHandle(PBoundsLocator.createEastLocator(node)); + PInputEventListener dragHandler = handle.getHandleDraggerHandler(); + PInputEventListener cursorHandler = handle.getHandleCursorEventHandler(); + assertNotNull(cursorHandler); + PInputEventListener[] listeners = handle.getInputEventListeners(); + assertEquals(2, listeners.length); + assertTrue(cursorHandler == listeners[0] || cursorHandler == listeners[1]); + assertTrue(dragHandler == listeners[0] || dragHandler == listeners[1]); + } +} diff --git a/swt/src/test/java/org/piccolo2d/extras/swt/PSWTCanvasTest.java b/swt/src/test/java/org/piccolo2d/extras/swt/PSWTCanvasTest.java new file mode 100644 index 0000000..3be15ef --- /dev/null +++ b/swt/src/test/java/org/piccolo2d/extras/swt/PSWTCanvasTest.java @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2008-2010, 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.extras.swt; + +import junit.framework.TestCase; + +import org.eclipse.swt.layout.FillLayout; + +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; +import org.piccolo2d.event.PInputEventListener; +import org.piccolo2d.event.PPanEventHandler; +import org.piccolo2d.event.PZoomEventHandler; +import org.piccolo2d.extras.swt.PSWTCanvas; + + +/** + * Unit test for PSWTCanvas. + */ +public class PSWTCanvasTest extends TestCase { + private PSWTCanvas canvas; + + public void setUp() { + final Shell shell = new Shell(Display.getDefault()); + shell.setLayout(new FillLayout()); + canvas = new PSWTCanvas(shell, 0); + } + + public void testPanEventListenerIsInstalledByDefault() { + PPanEventHandler handler = canvas.getPanEventHandler(); + assertNotNull(handler); + + int handlerIndex = getHandlerIndex(handler); + assertFalse("Pan Event Handler not installed", handlerIndex == -1); + } + + public void testZoomEventListenerIsInstalledByDefault() { + PZoomEventHandler handler = canvas.getZoomEventHandler(); + assertNotNull(handler); + + int handlerIndex = getHandlerIndex(handler); + assertFalse("Zoom Event Handler not installed", handlerIndex == -1); + } + + private int getHandlerIndex(PInputEventListener handler) { + PInputEventListener[] listeners = canvas.getCamera().getInputEventListeners(); + int handlerIndex = -1; + for (int i = 0; i < listeners.length; i++) { + if (listeners[i] == handler) { + handlerIndex = i; + } + } + return handlerIndex; + } + + public void testAnimatingDefaultsToFalse() { + assertFalse(canvas.getAnimating()); + } + + public void testInteractingDefaultsToFalse() { + assertFalse(canvas.getInteracting()); + } + + public void testInteractingWorksByCountingCallsToSetInteracting() { + canvas.setInteracting(true); + assertTrue(canvas.getInteracting()); + + canvas.setInteracting(true); + assertTrue(canvas.getInteracting()); + + canvas.setInteracting(false); + // This is terrible + assertTrue(canvas.getInteracting()); + + canvas.setInteracting(false); + assertFalse(canvas.getInteracting()); + } + + public void testCanvasIsDoubleBufferedByDefault() { + assertTrue(canvas.getDoubleBuffered()); + } + + public void testDoubleBufferingPersists() { + canvas.setDoubleBuffered(false); + assertFalse(canvas.getDoubleBuffered()); + canvas.setDoubleBuffered(true); + assertTrue(canvas.getDoubleBuffered()); + } +} diff --git a/swt/src/test/java/org/piccolo2d/extras/swt/PSWTHandleTest.java b/swt/src/test/java/org/piccolo2d/extras/swt/PSWTHandleTest.java new file mode 100644 index 0000000..53f2e18 --- /dev/null +++ b/swt/src/test/java/org/piccolo2d/extras/swt/PSWTHandleTest.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2008-2010, 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.extras.swt; + +import org.piccolo2d.PNode; +import org.piccolo2d.event.PInputEventListener; +import org.piccolo2d.extras.swt.PSWTHandle; +import org.piccolo2d.extras.util.PBoundsLocator; +import org.piccolo2d.extras.util.PLocator; + +import junit.framework.TestCase; + + + + +/** + * Unit test for PSWTHandle. + */ +public class PSWTHandleTest extends TestCase { + private PNode node; + private PSWTHandle handle; + private PBoundsLocator locator; + + public void setUp() throws Exception { + node = new PNode(); + locator = PBoundsLocator.createEastLocator(node); + handle = new PSWTHandle(locator); + node.setBounds(0, 0, 100, 100); + node.addChild(handle); + } + + public void testDefaultsAreCorrect() { + assertEquals(PSWTHandle.DEFAULT_COLOR, handle.getPaint()); + assertEquals(PSWTHandle.DEFAULT_HANDLE_SIZE + 2 /** for border pen */ + , handle.getHeight(), Float.MIN_VALUE); + } + + public void testLocatorPersists() { + assertSame(locator, handle.getLocator()); + + PLocator newLocator = PBoundsLocator.createWestLocator(node); + handle.setLocator(newLocator); + assertSame(newLocator, handle.getLocator()); + } + + public void testHandleHasDragHandlerInstalled() { + PInputEventListener dragHandler = handle.getHandleDraggerHandler(); + assertNotNull(dragHandler); + + PInputEventListener[] installedListeners = handle.getInputEventListeners(); + assertEquals(1, installedListeners.length); + assertSame(dragHandler, installedListeners[0]); + } + + public void testChangingParentDoesNotChangeLocatorNode() { + handle.relocateHandle(); + PNode newParent = new PNode(); + newParent.setBounds(50, 50, 100, 100); + + final double originalX = handle.getX(); + handle.setParent(newParent); + + final double newX = handle.getX(); + + assertEquals(newX, originalX, Double.MIN_VALUE); + } +} diff --git a/swt/src/test/java/org/piccolo2d/extras/swt/PSWTImageTest.java b/swt/src/test/java/org/piccolo2d/extras/swt/PSWTImageTest.java new file mode 100644 index 0000000..afd8b0b --- /dev/null +++ b/swt/src/test/java/org/piccolo2d/extras/swt/PSWTImageTest.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2008-2010, 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.extras.swt; + +import java.io.File; + +import junit.framework.TestCase; + +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.Rectangle; + +import org.eclipse.swt.layout.FillLayout; + +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; +import org.piccolo2d.extras.swt.PSWTCanvas; +import org.piccolo2d.extras.swt.PSWTImage; + +/** + * Unit test for PSWTImage. + */ +public class PSWTImageTest extends TestCase { + File imageFile; + PSWTCanvas canvas; + PSWTImage imageNode; + Image image; + + public void setUp() throws Exception { + final Display display = Display.getDefault(); + final Shell shell = new Shell(display); + shell.setLayout(new FillLayout()); + canvas = new PSWTCanvas(shell, 0); + imageNode = new PSWTImage(canvas); + image = new Image(display, new Rectangle(0, 0, 100, 100)); + } + + public void testImageShouldDefaultToNull() { + assertNull(imageNode.getImage()); + } + + public void testPaintShouldDoNothingWhenImageIsNull() { + // if it tries to use the graphics context, it would throw a NPE + imageNode.paint(null); + } + + public void testImageInConstructorPersists() { + imageNode = new PSWTImage(canvas, image); + assertSame(image, imageNode.getImage()); + } + + public void testDisposingCanvasDisposesImage() { + final boolean[] called = new boolean[1]; + called[0] = false; + imageNode = new PSWTImage(canvas, image) { + protected void disposeImage() { + called[0] = true; + super.disposeImage(); + } + }; + canvas.dispose(); + assertTrue(called[0]); + } +} diff --git a/swt/src/test/java/org/piccolo2d/extras/swt/PSWTPathTest.java b/swt/src/test/java/org/piccolo2d/extras/swt/PSWTPathTest.java new file mode 100755 index 0000000..80938b8 --- /dev/null +++ b/swt/src/test/java/org/piccolo2d/extras/swt/PSWTPathTest.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2008-2010, 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.extras.swt; + +import java.awt.geom.Point2D; + +import org.piccolo2d.extras.swt.PSWTPath; +import org.piccolo2d.util.PBounds; + +import junit.framework.TestCase; + + +/** + * Unit test for PSWTPath. + */ +public class PSWTPathTest extends TestCase { + + public void testCenterEmpty() { + PSWTPath path = new PSWTPath(); + + PBounds bounds = path.getBoundsReference(); + assertEquals(0.0d, bounds.getX(), 0.1d); + assertEquals(0.0d, bounds.getY(), 0.1d); + assertEquals(0.0d, bounds.getHeight(), 0.1d); + assertEquals(0.0d, bounds.getWidth(), 0.1d); + + Point2D center = path.getCenter(); + assertEquals(0.0d, center.getX(), 0.1d); + assertEquals(0.0d, center.getY(), 0.1d); + } + + public void testCenter() { + PSWTPath path = PSWTPath.createRectangle(10.0f, 20.0f, 100.0f, 200.0f); + + PBounds bounds = path.getBoundsReference(); + // hard to believe the tolerance in SWT is this poor + assertEquals(10.0d, bounds.getX(), 1.0d); + assertEquals(20.0d, bounds.getY(), 1.0d); + assertEquals(200.0d, bounds.getHeight(), 2.0d); + assertEquals(100.0d, bounds.getWidth(), 2.0d); + + Point2D center = path.getCenter(); + assertEquals(60.0d, center.getX(), 0.1d); + assertEquals(120.0d, center.getY(), 0.1d); + } + + public void testCenterScale() { + PSWTPath path = PSWTPath.createRectangle(10.0f, 20.0f, 100.0f, 200.0f); + path.scale(10.0d); + + PBounds bounds = path.getBoundsReference(); + // hard to believe the tolerance in SWT is this poor + assertEquals(10.0d, bounds.getX(), 1.0d); + assertEquals(20.0d, bounds.getY(), 1.0d); + assertEquals(200.0d, bounds.getHeight(), 2.0d); + assertEquals(100.0d, bounds.getWidth(), 2.0d); + + // center is calculated in terms of local bounds, not full bounds + Point2D center = path.getCenter(); + assertEquals(60.0d, center.getX(), 0.1d); + assertEquals(120.0d, center.getY(), 0.1d); + } + + public void testCenterRotate() { + PSWTPath path = PSWTPath.createRectangle(10.0f, 20.0f, 100.0f, 200.0f); + path.rotate(Math.PI / 8.0d); + + PBounds bounds = path.getBoundsReference(); + // hard to believe the tolerance in SWT is this poor + assertEquals(10.0d, bounds.getX(), 1.0d); + assertEquals(20.0d, bounds.getY(), 1.0d); + assertEquals(200.0d, bounds.getHeight(), 2.0d); + assertEquals(100.0d, bounds.getWidth(), 2.0d); + + // center is calculated in terms of local bounds, not full bounds + Point2D center = path.getCenter(); + assertEquals(60.0d, center.getX(), 0.1d); + assertEquals(120.0d, center.getY(), 0.1d); + } +} diff --git a/swt/src/test/java/org/piccolo2d/extras/swt/PSWTTextTest.java b/swt/src/test/java/org/piccolo2d/extras/swt/PSWTTextTest.java new file mode 100644 index 0000000..6ed2c75 --- /dev/null +++ b/swt/src/test/java/org/piccolo2d/extras/swt/PSWTTextTest.java @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2008-2010, 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.extras.swt; + +import java.awt.Color; +import java.awt.Font; + +import java.awt.geom.Point2D; + +import org.piccolo2d.extras.swt.PSWTText; +import org.piccolo2d.util.PBounds; + +import junit.framework.TestCase; + + +/** + * Unit test for PSWTText. + */ +public class PSWTTextTest extends TestCase { + private PSWTText textNode; + + public void setUp() { + textNode = new PSWTText(); + } + + public void testConstructorRemembersTextValue() { + textNode = new PSWTText("Hello World\n\n"); + assertEquals("Hello World\n\n", textNode.getText()); + } + + public void testTextPersistsTrainingAndInternalNewlines() { + textNode.setText("Hello\nWorld\n\n"); + assertEquals("Hello\nWorld\n\n", textNode.getText()); + } + + public void testDefaultPropertiesAreCorrect() { + assertEquals(Color.BLACK, textNode.getPenColor()); + assertEquals(Color.BLACK, textNode.getPenPaint()); + assertNull(textNode.getBackgroundColor()); + assertNull(textNode.getPaint()); + assertEquals(5.5, textNode.getGreekThreshold(), Double.MIN_VALUE); + assertFalse(textNode.isTransparent()); + } + + public void testDefaultFontIsCorrect() { + Font font = textNode.getFont(); + assertNotNull(font); + assertFalse(font.isBold()); + assertEquals(12, font.getSize()); + } + + public void testPenColorPersists() { + textNode.setPenColor(Color.RED); + assertEquals(Color.RED, textNode.getPenColor()); + } + + public void testPenPaintPersists() { + textNode.setPenPaint(Color.RED); + assertEquals(Color.RED, textNode.getPenPaint()); + } + + public void testTransparencyPersists() { + textNode.setTransparent(true); + assertTrue(textNode.isTransparent()); + } + + public void testBackgroundColor() { + textNode.setBackgroundColor(Color.RED); + assertEquals(Color.RED, textNode.getBackgroundColor()); + } + + public void testPenPaintAndPenColorAreSameThing() { + textNode.setPenColor(Color.RED); + assertEquals(Color.RED, textNode.getPenPaint()); + + textNode.setPenPaint(Color.BLUE); + assertEquals(Color.BLUE, textNode.getPenColor()); + } + + public void testBackgroundColorAndPaintAreSameThing() { + textNode.setBackgroundColor(Color.RED); + assertEquals(Color.RED, textNode.getPaint()); + + textNode.setPaint(Color.BLUE); + assertEquals(Color.BLUE, textNode.getBackgroundColor()); + } + + public void testGreekThresholdPersists() { + textNode.setGreekThreshold(0.1); + assertEquals(0.1, textNode.getGreekThreshold(), Double.MIN_VALUE); + } + + public void testShrinkingFontShrinksBounds() { + textNode.setText("Hello\nWorld"); + + PBounds startBounds = textNode.getBounds(); + Font startFont = textNode.getFont(); + Font newFont = new Font(startFont.getFontName(), startFont.getStyle(), 8); + + textNode.setFont(newFont); + assertSame(newFont, textNode.getFont()); + + PBounds endBounds = textNode.getBounds(); + assertTrue(startBounds.width > endBounds.width); + assertTrue(startBounds.height > endBounds.height); + } + + public void testTranslationsBehaveLogically() { + textNode.setTranslation(1, 2); + assertEquals(1, textNode.getTranslateX(), Double.MIN_VALUE); + assertEquals(2, textNode.getTranslateY(), Double.MIN_VALUE); + + textNode.setTranslateX(3); + assertEquals(3, textNode.getTranslateX(), Double.MIN_VALUE); + + textNode.setTranslateY(4); + assertEquals(4, textNode.getTranslateY(), Double.MIN_VALUE); + + assertEquals(new Point2D.Double(3, 4), textNode.getTranslation()); + + textNode.setTranslation(new Point2D.Double(5, 6)); + assertEquals(new Point2D.Double(5, 6), textNode.getTranslation()); + } + + public void testTranslatingDoesntAffectSize() { + textNode.setText("Hello"); + PBounds startBounds = textNode.getBounds(); + textNode.translate(1, 2); + PBounds endBounds = textNode.getBounds(); + assertEquals(startBounds.width, endBounds.width, Double.MIN_VALUE); + assertEquals(startBounds.height, endBounds.height, Double.MIN_VALUE); + } + +}