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