diff --git a/.gitignore b/.gitignore index f410d31a..60fa2795 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ target/ # strange - where comes this from? core/C:\\test.jpg +/bin/ diff --git a/core/src/main/java/org/piccolo2d/PApplet.java b/core/src/main/java/org/piccolo2d/PApplet.java new file mode 100644 index 00000000..d99b8cd1 --- /dev/null +++ b/core/src/main/java/org/piccolo2d/PApplet.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2008-2019, 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; + +import javax.swing.JApplet; +import javax.swing.SwingUtilities; + +/** + * PApplet is meant to be subclassed by applications that just need a + * PCanvas embedded in a web page. + * + * @version 1.0 + * @author Jesse Grosjean + */ +public class PApplet extends JApplet { + /** Used to allow versioned binary streams for serializations. */ + private static final long serialVersionUID = 1L; + + /** Canvas being displayed by this applet. */ + private PCanvas canvas; + + /** + * Initializes the applet with a canvas and no background. + */ + public void init() { + setBackground(null); + + canvas = createCanvas(); + getContentPane().add(canvas); + validate(); + canvas.requestFocus(); + beforeInitialize(); + + // Manipulation of Piccolo's scene graph should be done from Swings + // event dispatch thread since Piccolo is not thread safe. This code + // calls initialize() from that thread once the PFrame is initialized, + // so you are safe to start working with Piccolo in the initialize() + // method. + SwingUtilities.invokeLater(new Runnable() { + public void run() { + PApplet.this.initialize(); + repaint(); + } + }); + } + + /** + * Returns the canvas this PApplet is displaying. + * + * @return canvas this applet is displaying + */ + public PCanvas getCanvas() { + return canvas; + } + + /** + * Provides an extension point for subclasses so that they can control what's on + * the canvas by default. + * + * @return a built canvas + */ + public PCanvas createCanvas() { + return new PCanvas(); + } + + /** + * This method will be called before the initialize() method and will be called + * on the thread that is constructing this object. + */ + public void beforeInitialize() { + } + + /** + * Subclasses should override this method and add their Piccolo2d initialization + * code there. This method will be called on the swing event dispatch thread. + * Note that the constructors of PFrame subclasses may not be complete when this + * method is called. If you need to initailize some things in your class before + * this method is called place that code in beforeInitialize(); + */ + public void initialize() { + } +} diff --git a/core/src/main/java/org/piccolo2d/PCamera.java b/core/src/main/java/org/piccolo2d/PCamera.java index 548b8d51..d7467314 100644 --- a/core/src/main/java/org/piccolo2d/PCamera.java +++ b/core/src/main/java/org/piccolo2d/PCamera.java @@ -52,7 +52,6 @@ import org.piccolo2d.util.PPickPath; import org.piccolo2d.util.PUtil; - /** * PCamera represents a viewport onto a list of layer nodes. Each camera * maintains a view transform through which it views these layers. Translating @@ -69,904 +68,897 @@ */ public class PCamera extends PNode { - /** Default serial version UID. */ - private static final long serialVersionUID = 1L; - - /** - * The property name that identifies a change in the set of this camera's - * layers (see {@link #getLayer getLayer}, {@link #getLayerCount - * getLayerCount}, {@link #getLayersReference getLayersReference}). A - * property change event's new value will be a reference to the list of this - * nodes layers, but old value will always be null. - */ - public static final String PROPERTY_LAYERS = "layers"; - - /** - * The property code that identifies a change in the set of this camera's - * layers (see {@link #getLayer getLayer}, {@link #getLayerCount - * getLayerCount}, {@link #getLayersReference getLayersReference}). A - * property change event's new value will be a reference to the list of this - * nodes layers, but old value will always be null. - */ - public static final int PROPERTY_CODE_LAYERS = 1 << 11; - - /** - * The property name that identifies a change in this camera's view - * transform (see {@link #getViewTransform getViewTransform}, - * {@link #getViewTransformReference getViewTransformReference}). A property - * change event's new value will be a reference to the view transform, but - * old value will always be null. - */ - public static final String PROPERTY_VIEW_TRANSFORM = "viewTransform"; - - /** - * The property code that identifies a change in this camera's view - * transform (see {@link #getViewTransform getViewTransform}, - * {@link #getViewTransformReference getViewTransformReference}). A property - * change event's new value will be a reference to the view transform, but - * old value will always be null. - */ - public static final int PROPERTY_CODE_VIEW_TRANSFORM = 1 << 12; - - /** Denotes that the view has no constraints. */ - public static final int VIEW_CONSTRAINT_NONE = 0; - - /** Enforces that the view be able to see all nodes in the scene. */ - public static final int VIEW_CONSTRAINT_ALL = 1; - - /** Constrains the the view to be centered on the scene's full bounds. */ - public static final int VIEW_CONSTRAINT_CENTER = 2; - - /** Component which receives repaint notification from this camera. */ - private transient PComponent component; - - /** List of layers viewed by this camera. */ - private transient List/**/ layers; - - /** - * Transform applied to layers before they are rendered. This transform - * differs from the transform applied to the children of this PCamera - * (sticky objects). - */ - private final PAffineTransform viewTransform; - - /** Constraints to apply to the viewing area. */ - private int viewConstraint; - - /** Temporary bounds used as an optimization during repaint. */ - private static final PBounds TEMP_REPAINT_RECT = new PBounds(); - - - /** - * Create a new camera with an empy list of layers. - */ - public PCamera() { - super(); - viewTransform = new PAffineTransform(); - layers = new ArrayList/**/(); - viewConstraint = VIEW_CONSTRAINT_NONE; - } - - - /** - * Return the component for this camera, or null if no - * component has been associated with this camera, as may be the case for - * internal cameras. - * - * @return the component for this camera, or null if no such - * component exists - */ - public PComponent getComponent() { - return component; - } - - /** - * Set the component for this camera to component. The - * component, if non-null, receives repaint notification from this camera. - * - * @param component component for this camera - */ - public void setComponent(final PComponent component) { - this.component = component; - invalidatePaint(); - } - - /** - * Repaint this camera and forward the repaint request to the component - * for this camera, if it is non-null. - * - * @param localBounds bounds that require repainting, in local coordinates - * @param sourceNode node from which the repaint message originates, may - * be the camera itself - */ - public void repaintFrom(final PBounds localBounds, final PNode sourceNode) { - if (getParent() != null) { - if (sourceNode != this) { - localToParent(localBounds); - } - if (component != null) { - component.repaint(localBounds); - } - getParent().repaintFrom(localBounds, this); - } - } - - /** - * Repaint from one of the camera's layers. The repaint region needs to be - * transformed from view to local in this case. Unlike most repaint methods - * in Piccolo2D this one must not modify the viewBounds - * parameter. - * - * @since 1.3 - * @param viewBounds bounds that require repainting, in view coordinates - * @param repaintedLayer layer dispatching the repaint notification - */ - public void repaintFromLayer(final PBounds viewBounds, final PLayer repaintedLayer) { - TEMP_REPAINT_RECT.setRect(viewBounds); - viewToLocal(TEMP_REPAINT_RECT); - if (getBoundsReference().intersects(TEMP_REPAINT_RECT)) { - Rectangle2D.intersect(TEMP_REPAINT_RECT, getBoundsReference(), TEMP_REPAINT_RECT); - repaintFrom(TEMP_REPAINT_RECT, repaintedLayer); - } - } - - /** - * Return a reference to the list of layers viewed by this camera. - * - * @return the list of layers viewed by this camera - */ - public List/**/ getLayersReference() { - return layers; - } - - /** - * Return the number of layers in the list of layers viewed by this camera. - * - * @return the number of layers in the list of layers viewed by this camera - */ - public int getLayerCount() { - return layers.size(); - } - - /** - * Return the layer at the specified position in the list of layers viewed by this camera. - * - * @param index index of the layer to return - * @return the layer at the specified position in the list of layers viewed by this camera - * @throws IndexOutOfBoundsException if the specified index is out of range - * (index < 0 || index >= getLayerCount()) - */ - public PLayer getLayer(final int index) { - return (PLayer) layers.get(index); - } - - /** - * Return the index of the first occurrence of the specified layer in the - * list of layers viewed by this camera, or -1 if the list of layers - * viewed by this camera does not contain the specified layer. - * - * @param layer layer to search for - * @return the index of the first occurrence of the specified layer in the - * list of layers viewed by this camera, or -1 if the list of - * layers viewed by this camera does not contain the specified layer - */ - public int indexOfLayer(final PLayer layer) { - return layers.indexOf(layer); - } - - /** - * Inserts the specified layer at the end of the list of layers viewed by this camera. - * Layers may be viewed by multiple cameras at once. - * - * @param layer layer to add - */ - public void addLayer(final PLayer layer) { - addLayer(layers.size(), layer); - } - - /** - * Inserts the specified layer at the specified position in the list of layers viewed by this camera. - * Layers may be viewed by multiple cameras at once. - * - * @param index index at which the specified layer is to be inserted - * @param layer layer to add - * @throws IndexOutOfBoundsException if the specified index is out of range - * (index < 0 || index >= getLayerCount()) - */ - public void addLayer(final int index, final PLayer layer) { - layers.add(index, layer); - layer.addCamera(this); - invalidatePaint(); - firePropertyChange(PROPERTY_CODE_LAYERS, PROPERTY_LAYERS, null, layers); - } - - /** - * Removes the first occurrence of the specified layer from the list of - * layers viewed by this camera, if it is present. - * - * @param layer layer to be removed - * @return the specified layer - */ - public PLayer removeLayer(final PLayer layer) { - layer.removeCamera(this); - if (layers.remove(layer)) { - invalidatePaint(); - firePropertyChange(PROPERTY_CODE_LAYERS, PROPERTY_LAYERS, null, layers); - } - return layer; - } - - /** - * Removes the element at the specified position from the list of layers - * viewed by this camera. - * - * @param index index of the layer to remove - * @return the layer previously at the specified position - * @throws IndexOutOfBoundsException if the specified index is out of range - * (index < 0 || index >= getLayerCount()) - */ - public PLayer removeLayer(final int index) { - final PLayer layer = (PLayer) layers.remove(index); - layer.removeCamera(this); - invalidatePaint(); - firePropertyChange(PROPERTY_CODE_LAYERS, PROPERTY_LAYERS, null, layers); - return layer; - } - - /** - * Return the union of the full bounds of each layer in the list of layers - * viewed by this camera, or empty bounds if the list of layers viewed by - * this camera is empty. - * - * @return the union of the full bounds of each layer in the list of layers - * viewed by this camera, or empty bounds if the list of layers viewed - * by this camera is empty - */ - public PBounds getUnionOfLayerFullBounds() { - final PBounds result = new PBounds(); - final int size = layers.size(); - for (int i = 0; i < size; i++) { - final PLayer each = (PLayer) layers.get(i); - result.add(each.getFullBoundsReference()); - } - return result; - } - - /** - * Paint this camera and then paint this camera's view through its view - * transform. - * - * @param paintContext context in which painting occurs - */ - protected void paint(final PPaintContext paintContext) { - super.paint(paintContext); - - paintContext.pushClip(getBoundsReference()); - paintContext.pushTransform(viewTransform); - - paintCameraView(paintContext); - paintDebugInfo(paintContext); - - paintContext.popTransform(viewTransform); - paintContext.popClip(getBoundsReference()); - } - - /** - * Paint all the layers in the list of layers viewed by this camera. This method - * is called after the view transform and clip have been applied to the - * specified paint context. - * - * @param paintContext context in which painting occurs - */ - protected void paintCameraView(final PPaintContext paintContext) { - final int size = layers.size(); - for (int i = 0; i < size; i++) { - final PLayer each = (PLayer) layers.get(i); - each.fullPaint(paintContext); - } - } - - /** - * Renders debug info onto the newly painted scene. Things like full bounds - * and bounds are painted as filled and outlines. - * - * @param paintContext context in which painting occurs - */ - protected void paintDebugInfo(final PPaintContext paintContext) { - if (PDebug.debugBounds || PDebug.debugFullBounds) { - final Graphics2D g2 = paintContext.getGraphics(); - paintContext.setRenderQuality(PPaintContext.LOW_QUALITY_RENDERING); - g2.setStroke(new BasicStroke(0)); - final ArrayList nodes = new ArrayList(); - final PBounds nodeBounds = new PBounds(); - - final Color boundsColor = Color.red; - final Color fullBoundsColor = new Color(1.0f, 0f, 0f, 0.2f); - - final int size = layers.size(); - for (int i = 0; i < size; i++) { - ((PLayer) layers.get(i)).getAllNodes(null, nodes); - } - - final Iterator i = getAllNodes(null, nodes).iterator(); - - while (i.hasNext()) { - final PNode each = (PNode) i.next(); - - if (PDebug.debugBounds) { - g2.setPaint(boundsColor); - nodeBounds.setRect(each.getBoundsReference()); - - if (!nodeBounds.isEmpty()) { - each.localToGlobal(nodeBounds); - globalToLocal(nodeBounds); - if (each == this || each.isDescendentOf(this)) { - localToView(nodeBounds); - } - g2.draw(nodeBounds); - } - } - - if (PDebug.debugFullBounds) { - g2.setPaint(fullBoundsColor); - nodeBounds.setRect(each.getFullBoundsReference()); - - if (!nodeBounds.isEmpty()) { - if (each.getParent() != null) { - each.getParent().localToGlobal(nodeBounds); - } - globalToLocal(nodeBounds); - if (each == this || each.isDescendentOf(this)) { - localToView(nodeBounds); - } - g2.fill(nodeBounds); - } - } - } - } - } - - /** - * {@inheritDoc} - * - *

- * Pushes this camera onto the specified paint context so that it - * can be accessed later by {@link PPaintContext#getCamera}. - *

- */ - public void fullPaint(final PPaintContext paintContext) { - paintContext.pushCamera(this); - super.fullPaint(paintContext); - paintContext.popCamera(); - } - - /** - * Generate and return a PPickPath for the point x,y specified in the local - * coord system of this camera. Picking is done with a rectangle, halo - * specifies how large that rectangle will be. - * - * @param x the x coordinate of the pick path given in local coordinates - * @param y the y coordinate of the pick path given in local coordinates - * @param halo the distance from the x,y coordinate that is considered for - * inclusion in the pick path - * - * @return the picked path - */ - public PPickPath pick(final double x, final double y, final double halo) { - final PBounds b = new PBounds(new Point2D.Double(x, y), -halo, -halo); - final PPickPath result = new PPickPath(this, b); - - fullPick(result); - - // make sure this camera is pushed. - if (result.getNodeStackReference().size() == 0) { - result.pushNode(this); - result.pushTransform(getTransformReference(false)); - } - - return result; - } - - /** - * {@inheritDoc} - * - *

- * After the direct children of this camera have been given a chance to be - * picked all of the layers in the list of layers viewed by this camera are - * given a chance to be picked. - *

- * - * @return true if any of the layers in the list of layers viewed by this - * camera were picked - */ - protected boolean pickAfterChildren(final PPickPath pickPath) { - if (intersects(pickPath.getPickBounds())) { - pickPath.pushTransform(viewTransform); - - if (pickCameraView(pickPath)) { - return true; - } - - pickPath.popTransform(viewTransform); - return true; - } - return false; - } - - /** - * Try to pick all of the layers in the list of layers viewed by this - * camera. This method is called after the view transform has been applied - * to the specified pick path. - * - * @param pickPath pick path - * @return true if any of the layers in the list of layers viewed by this - * camera were picked - */ - protected boolean pickCameraView(final PPickPath pickPath) { - final int size = layers.size(); - for (int i = size - 1; i >= 0; i--) { - final PLayer each = (PLayer) layers.get(i); - if (each.fullPick(pickPath)) { - return true; - } - } - return false; - } - - // **************************************************************** - // View Transform - Methods for accessing the view transform. The - // view transform is applied before painting and picking the cameras - // layers. But not before painting or picking its direct children. - // - // Changing the view transform is how zooming and panning are - // accomplished. - // **************************************************************** - - /** - * Return the bounds of this camera in the view coordinate system. - * - * @return the bounds of this camera in the view coordinate system - */ - public PBounds getViewBounds() { - return (PBounds) localToView(getBounds()); - } - - /** - * Animates the camera's view so that the given bounds (in camera layer's - * coordinate system) are centered within the cameras view bounds. Use this - * method to point the camera at a given location. - * - * @param centerBounds the targetBounds - */ - public void setViewBounds(final Rectangle2D centerBounds) { - animateViewToCenterBounds(centerBounds, true, 0); - } - - /** - * Return the scale applied by the view transform to the list of layers - * viewed by this camera. - * - * @return the scale applied by the view transform to the list of layers - * viewed by this camera - */ - public double getViewScale() { - return viewTransform.getScale(); - } - - /** - * Scale the view transform applied to the list of layers viewed by this - * camera by scale about the point [0, 0]. - * - * @param scale view transform scale - */ - public void scaleView(final double scale) { - scaleViewAboutPoint(scale, 0, 0); - } - - /** - * Scale the view transform applied to the list of layers viewed by this - * camera by scale about the specified point - * [x, y]. - * - * @param scale view transform scale - * @param x scale about point, x coordinate - * @param y scale about point, y coordinate - */ - public void scaleViewAboutPoint(final double scale, final double x, final double y) { - viewTransform.scaleAboutPoint(scale, x, y); - applyViewConstraints(); - invalidatePaint(); - firePropertyChange(PROPERTY_CODE_VIEW_TRANSFORM, PROPERTY_VIEW_TRANSFORM, null, viewTransform); - } - - /** - * Set the scale applied by the view transform to the list of layers - * viewed by this camera to scale. - * - * @param scale view transform scale - */ - public void setViewScale(final double scale) { - scaleView(scale / getViewScale()); - } - - /** - * Translate the view transform applied to the list of layers viewed by this - * camera by [dx, dy]. - * - * @param dx translate delta x - * @param dy translate delta y - */ - public void translateView(final double dx, final double dy) { - viewTransform.translate(dx, dy); - applyViewConstraints(); - invalidatePaint(); - firePropertyChange(PROPERTY_CODE_VIEW_TRANSFORM, PROPERTY_VIEW_TRANSFORM, null, viewTransform); - } - - /** - * Offset the view transform applied to the list of layers viewed by this camera by [dx, dy]. This is - * NOT effected by the view transform's current scale or rotation. This is implemented by directly adding dx to the - * m02 position and dy to the m12 position in the affine transform. - * - * @param dx offset delta x - * @param dy offset delta y - */ - /* - public void offsetView(final double dx, final double dy) { - setViewOffset(viewTransform.getTranslateX() + dx, viewTransform.getTranslateY() + dy); - } - */ - - /** - * Set the offset for the view transform applied to the list of layers - * viewed by this camera to [x, y]. - * - * @param x offset x - * @param y offset y - */ - public void setViewOffset(final double x, final double y) { - viewTransform.setOffset(x, y); - applyViewConstraints(); - invalidatePaint(); - firePropertyChange(PROPERTY_CODE_VIEW_TRANSFORM, PROPERTY_VIEW_TRANSFORM, null, viewTransform); - } - - /** - * Return a copy of the view transform applied to the list of layers - * viewed by this camera. - * - * @return a copy of the view transform applied to the list of layers - * viewed by this camera - */ - public PAffineTransform getViewTransform() { - return (PAffineTransform) viewTransform.clone(); - } - - /** - * Return a reference to the view transform applied to the list of layers - * viewed by this camera. - * - * @return the view transform applied to the list of layers - * viewed by this camera - */ - public PAffineTransform getViewTransformReference() { - return viewTransform; - } - - /** - * Set the view transform applied to the list of layers - * viewed by this camera to viewTransform. - * - * @param viewTransform view transform applied to the list of layers - * viewed by this camera - */ - public void setViewTransform(final AffineTransform viewTransform) { - this.viewTransform.setTransform(viewTransform); - applyViewConstraints(); - invalidatePaint(); - firePropertyChange(PROPERTY_CODE_VIEW_TRANSFORM, PROPERTY_VIEW_TRANSFORM, null, this.viewTransform); - } - - /** - * Animate the camera's view from its current transform when the activity - * starts to a new transform that centers the given bounds in the camera - * layer's coordinate system into the cameras view bounds. If the duration is - * 0 then the view will be transformed immediately, and null will be - * returned. Else a new PTransformActivity will get returned that is set to - * animate the camera's view transform to the new bounds. If shouldScale is - * true, then the camera will also scale its view so that the given bounds - * fit fully within the cameras view bounds, else the camera will maintain - * its original scale. - * - * @param centerBounds the bounds which the animation will pace at the - * center of the view - * @param shouldScaleToFit whether the camera should scale the view while - * animating to it - * @param duration how many milliseconds the animations should take - * - * @return the scheduled PTransformActivity - */ - public PTransformActivity animateViewToCenterBounds(final Rectangle2D centerBounds, final boolean shouldScaleToFit, - final long duration) { - final PBounds viewBounds = getViewBounds(); - final PDimension delta = viewBounds.deltaRequiredToCenter(centerBounds); - final PAffineTransform newTransform = getViewTransform(); - newTransform.translate(delta.width, delta.height); - - if (shouldScaleToFit) { - final double s = Math.min(viewBounds.getWidth() / centerBounds.getWidth(), viewBounds.getHeight() - / centerBounds.getHeight()); - if (s != Double.POSITIVE_INFINITY && s != 0) { - newTransform.scaleAboutPoint(s, centerBounds.getCenterX(), centerBounds.getCenterY()); - } - } - - return animateViewToTransform(newTransform, duration); - } - - /** - * Pan the camera's view from its current transform when the activity starts - * to a new transform so that the view bounds will contain (if possible, - * intersect if not possible) the new bounds in the camera layers coordinate - * system. If the duration is 0 then the view will be transformed - * immediately, and null will be returned. Else a new PTransformActivity - * will get returned that is set to animate the camera's view transform to - * the new bounds. - * - * @param panToBounds the bounds to which the view will animate to - * @param duration the duration of the animation given in milliseconds - * - * @return the scheduled PTransformActivity - */ - public PTransformActivity animateViewToPanToBounds(final Rectangle2D panToBounds, final long duration) { - final PBounds viewBounds = getViewBounds(); - final PDimension delta = viewBounds.deltaRequiredToContain(panToBounds); - - if (delta.width != 0 || delta.height != 0) { - if (duration == 0) { - translateView(-delta.width, -delta.height); - } - else { - final AffineTransform at = getViewTransform(); - at.translate(-delta.width, -delta.height); - return animateViewToTransform(at, duration); - } - } - - return null; - } - - /** - * Animate the cameras view transform from its current value when the - * activity starts to the new destination transform value. - * - * @param destination the transform to which the view should be transformed - * into - * @param duration the duraiton in milliseconds the animation should take - * - * @return the scheduled PTransformActivity - */ - public PTransformActivity animateViewToTransform(final AffineTransform destination, final long duration) { - if (duration == 0) { - setViewTransform(destination); - return null; - } - - final PTransformActivity.Target t = new PTransformActivity.Target() { - /** {@inheritDoc} */ - public void setTransform(final AffineTransform aTransform) { - PCamera.this.setViewTransform(aTransform); - } - - /** {@inheritDoc} */ - public void getSourceMatrix(final double[] aSource) { - viewTransform.getMatrix(aSource); - } - }; - - final PTransformActivity transformActivity = new PTransformActivity(duration, PUtil.DEFAULT_ACTIVITY_STEP_RATE, - t, destination); - - final PRoot r = getRoot(); - if (r != null) { - r.getActivityScheduler().addActivity(transformActivity); - } - - return transformActivity; - } - - // **************************************************************** - // View Transform Constraints - Methods for setting and applying - // constraints to the view transform. - // **************************************************************** - - /** - * Return the constraint applied to the view. The view constraint will be one of {@link #VIEW_CONSTRAINT_NONE}, - * {@link #VIEW_CONSTRAINT_CENTER}, or {@link #VIEW_CONSTRAINT_CENTER}. Defaults to {@link #VIEW_CONSTRAINT_NONE}. - * - * @return the view constraint being applied to the view - */ - public int getViewConstraint() { - return viewConstraint; - } - - /** - * Set the view constraint to apply to the view to viewConstraint. The view constraint must be one of - * {@link #VIEW_CONSTRAINT_NONE}, {@link #VIEW_CONSTRAINT_CENTER}, or {@link #VIEW_CONSTRAINT_CENTER}. - * - * @param viewConstraint constraint to apply to the view - * @throws IllegalArgumentException if viewConstraint is not one of {@link #VIEW_CONSTRAINT_NONE}, - * {@link #VIEW_CONSTRAINT_CENTER}, or {@link #VIEW_CONSTRAINT_CENTER} - */ - public void setViewConstraint(final int viewConstraint) { - if (viewConstraint != VIEW_CONSTRAINT_NONE && viewConstraint != VIEW_CONSTRAINT_CENTER - && viewConstraint != VIEW_CONSTRAINT_ALL) { - throw new IllegalArgumentException("view constraint must be one " - + "of VIEW_CONSTRAINT_NONE, VIEW_CONSTRAINT_CENTER, or VIEW_CONSTRAINT_ALL"); - } - this.viewConstraint = viewConstraint; - applyViewConstraints(); - } - - /** - * Transforms the view so that it conforms to the given constraint. - */ - protected void applyViewConstraints() { - if (VIEW_CONSTRAINT_NONE == viewConstraint) { - return; - } - final PBounds viewBounds = getViewBounds(); - final PBounds layerBounds = (PBounds) globalToLocal(getUnionOfLayerFullBounds()); - - if (VIEW_CONSTRAINT_CENTER == viewConstraint) { - layerBounds.setRect(layerBounds.getCenterX(), layerBounds.getCenterY(), 0, 0); - } - PDimension constraintDelta = viewBounds.deltaRequiredToContain(layerBounds); - viewTransform.translate(-constraintDelta.width, -constraintDelta.height); - } - - // **************************************************************** - // Camera View Coord System Conversions - Methods to translate from - // the camera's local coord system (above the camera's view transform) to - // the - // camera view coord system (below the camera's view transform). When - // converting geometry from one of the canvas's layers you must go - // through the view transform. - // **************************************************************** - - /** - * Convert the point from the camera's view coordinate system to the - * camera's local coordinate system. The given point is modified by this. - * - * @param viewPoint the point to transform to the local coordinate system - * from the view's coordinate system - * @return the transformed point - */ - public Point2D viewToLocal(final Point2D viewPoint) { - return viewTransform.transform(viewPoint, viewPoint); - } - - /** - * Convert the dimension from the camera's view coordinate system to the - * camera's local coordinate system. The given dimension is modified by - * this. - * - * @param viewDimension the dimension to transform from the view system to - * the local coordinate system - * - * @return returns the transformed dimension - */ - public Dimension2D viewToLocal(final Dimension2D viewDimension) { - return viewTransform.transform(viewDimension, viewDimension); - } - - /** - * Convert the rectangle from the camera's view coordinate system to the - * camera's local coordinate system. The given rectangle is modified by this - * method. - * - * @param viewRectangle the rectangle to transform from view to local - * coordinate System - * @return the transformed rectangle - */ - public Rectangle2D viewToLocal(final Rectangle2D viewRectangle) { - return viewTransform.transform(viewRectangle, viewRectangle); - } - - /** - * Convert the point from the camera's local coordinate system to the - * camera's view coordinate system. The given point is modified by this - * method. - * - * @param localPoint point to transform from local to view coordinate system - * @return the transformed point - */ - public Point2D localToView(final Point2D localPoint) { - return viewTransform.inverseTransform(localPoint, localPoint); - } - - /** - * Convert the dimension from the camera's local coordinate system to the - * camera's view coordinate system. The given dimension is modified by this - * method. - * - * @param localDimension the dimension to transform from local to view - * coordinate systems - * @return the transformed dimension - */ - public Dimension2D localToView(final Dimension2D localDimension) { - return viewTransform.inverseTransform(localDimension, localDimension); - } - - /** - * Convert the rectangle from the camera's local coordinate system to the - * camera's view coordinate system. The given rectangle is modified by this - * method. - * - * @param localRectangle the rectangle to transform from local to view - * coordinate system - * @return the transformed rectangle - */ - public Rectangle2D localToView(final Rectangle2D localRectangle) { - return viewTransform.inverseTransform(localRectangle, localRectangle); - } - - // **************************************************************** - // Serialization - Cameras conditionally serialize their layers. - // This means that only the layer references that were unconditionally - // (using writeObject) serialized by someone else will be restored - // when the camera is unserialized. - // ****************************************************************/ - - /** - * Write this camera and all its children out to the given stream. Note that - * the cameras layers are written conditionally, so they will only get - * written out if someone else writes them unconditionally. - * - * @param out the PObjectOutputStream to which this camera should be - * serialized - * @throws IOException if an error occured writing to the output stream - */ - private void writeObject(final ObjectOutputStream out) throws IOException { - if (!(out instanceof PObjectOutputStream)) { - throw new RuntimeException("cannot serialize PCamera to a non PObjectOutputStream"); - } - out.defaultWriteObject(); - - final int count = getLayerCount(); - for (int i = 0; i < count; i++) { - ((PObjectOutputStream) out).writeConditionalObject(layers.get(i)); - } - - out.writeObject(Boolean.FALSE); - ((PObjectOutputStream) out).writeConditionalObject(component); - } - - /** - * Deserializes this PCamera from the ObjectInputStream. - * - * @param in the source ObjectInputStream - * @throws IOException when error occurs during read - * @throws ClassNotFoundException if the stream attempts to deserialize a - * missing class - */ - private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { - in.defaultReadObject(); - - layers = new ArrayList(); - - while (true) { - final Object each = in.readObject(); - if (each != null) { - if (each.equals(Boolean.FALSE)) { - break; - } - else { - layers.add(each); - } - } - } - - component = (PComponent) in.readObject(); - } + /** Default serial version UID. */ + private static final long serialVersionUID = 1L; + + /** + * The property name that identifies a change in the set of this camera's layers + * (see {@link #getLayer getLayer}, {@link #getLayerCount getLayerCount}, + * {@link #getLayersReference getLayersReference}). A property change event's + * new value will be a reference to the list of this nodes layers, but old value + * will always be null. + */ + public static final String PROPERTY_LAYERS = "layers"; + + /** + * The property code that identifies a change in the set of this camera's layers + * (see {@link #getLayer getLayer}, {@link #getLayerCount getLayerCount}, + * {@link #getLayersReference getLayersReference}). A property change event's + * new value will be a reference to the list of this nodes layers, but old value + * will always be null. + */ + public static final int PROPERTY_CODE_LAYERS = 1 << 11; + + /** + * The property name that identifies a change in this camera's view transform + * (see {@link #getViewTransform getViewTransform}, + * {@link #getViewTransformReference getViewTransformReference}). A property + * change event's new value will be a reference to the view transform, but old + * value will always be null. + */ + public static final String PROPERTY_VIEW_TRANSFORM = "viewTransform"; + + /** + * The property code that identifies a change in this camera's view transform + * (see {@link #getViewTransform getViewTransform}, + * {@link #getViewTransformReference getViewTransformReference}). A property + * change event's new value will be a reference to the view transform, but old + * value will always be null. + */ + public static final int PROPERTY_CODE_VIEW_TRANSFORM = 1 << 12; + + /** Denotes that the view has no constraints. */ + public static final int VIEW_CONSTRAINT_NONE = 0; + + /** Enforces that the view be able to see all nodes in the scene. */ + public static final int VIEW_CONSTRAINT_ALL = 1; + + /** Constrains the the view to be centered on the scene's full bounds. */ + public static final int VIEW_CONSTRAINT_CENTER = 2; + + /** Component which receives repaint notification from this camera. */ + private transient PComponent component; + + /** List of layers viewed by this camera. */ + private transient List layers; + + /** + * Transform applied to layers before they are rendered. This transform differs + * from the transform applied to the children of this PCamera (sticky objects). + */ + private final PAffineTransform viewTransform; + + /** Constraints to apply to the viewing area. */ + private int viewConstraint; + + /** Temporary bounds used as an optimization during repaint. */ + private static final PBounds TEMP_REPAINT_RECT = new PBounds(); + + /** + * Create a new camera with an empy list of layers. + */ + public PCamera() { + super(); + viewTransform = new PAffineTransform(); + layers = new ArrayList(); + viewConstraint = VIEW_CONSTRAINT_NONE; + } + + /** + * Return the component for this camera, or null if no component + * has been associated with this camera, as may be the case for internal + * cameras. + * + * @return the component for this camera, or null if no such + * component exists + */ + public PComponent getComponent() { + return component; + } + + /** + * Set the component for this camera to component. The component, + * if non-null, receives repaint notification from this camera. + * + * @param component component for this camera + */ + public void setComponent(final PComponent component) { + this.component = component; + invalidatePaint(); + } + + /** + * Repaint this camera and forward the repaint request to the component for this + * camera, if it is non-null. + * + * @param localBounds bounds that require repainting, in local coordinates + * @param sourceNode node from which the repaint message originates, may be the + * camera itself + */ + public void repaintFrom(final PBounds localBounds, final PNode sourceNode) { + if (getParent() != null) { + if (sourceNode != this) { + localToParent(localBounds); + } + if (component != null) { + component.repaint(localBounds); + } + getParent().repaintFrom(localBounds, this); + } + } + + /** + * Repaint from one of the camera's layers. The repaint region needs to be + * transformed from view to local in this case. Unlike most repaint methods in + * Piccolo2D this one must not modify the viewBounds parameter. + * + * @since 1.3 + * @param viewBounds bounds that require repainting, in view coordinates + * @param repaintedLayer layer dispatching the repaint notification + */ + public void repaintFromLayer(final PBounds viewBounds, final PLayer repaintedLayer) { + TEMP_REPAINT_RECT.setRect(viewBounds); + viewToLocal(TEMP_REPAINT_RECT); + if (getBoundsReference().intersects(TEMP_REPAINT_RECT)) { + Rectangle2D.intersect(TEMP_REPAINT_RECT, getBoundsReference(), TEMP_REPAINT_RECT); + repaintFrom(TEMP_REPAINT_RECT, repaintedLayer); + } + } + + /** + * Return a reference to the list of layers viewed by this camera. + * + * @return the list of layers viewed by this camera + */ + public List getLayersReference() { + return layers; + } + + /** + * Return the number of layers in the list of layers viewed by this camera. + * + * @return the number of layers in the list of layers viewed by this camera + */ + public int getLayerCount() { + return layers.size(); + } + + /** + * Return the layer at the specified position in the list of layers viewed by + * this camera. + * + * @param index index of the layer to return + * @return the layer at the specified position in the list of layers viewed by + * this camera + * @throws IndexOutOfBoundsException if the specified index is out of range + * (index < 0 || index >= getLayerCount()) + */ + public PLayer getLayer(final int index) { + return (PLayer) layers.get(index); + } + + /** + * Return the index of the first occurrence of the specified layer in the list + * of layers viewed by this camera, or -1 if the list of layers + * viewed by this camera does not contain the specified layer. + * + * @param layer layer to search for + * @return the index of the first occurrence of the specified layer in the list + * of layers viewed by this camera, or -1 if the list of + * layers viewed by this camera does not contain the specified layer + */ + public int indexOfLayer(final PLayer layer) { + return layers.indexOf(layer); + } + + /** + * Inserts the specified layer at the end of the list of layers viewed by this + * camera. Layers may be viewed by multiple cameras at once. + * + * @param layer layer to add + */ + public void addLayer(final PLayer layer) { + addLayer(layers.size(), layer); + } + + /** + * Inserts the specified layer at the specified position in the list of layers + * viewed by this camera. Layers may be viewed by multiple cameras at once. + * + * @param index index at which the specified layer is to be inserted + * @param layer layer to add + * @throws IndexOutOfBoundsException if the specified index is out of range + * (index < 0 || index >= getLayerCount()) + */ + public void addLayer(final int index, final PLayer layer) { + layers.add(index, layer); + layer.addCamera(this); + invalidatePaint(); + firePropertyChange(PROPERTY_CODE_LAYERS, PROPERTY_LAYERS, null, layers); + } + + /** + * Removes the first occurrence of the specified layer from the list of layers + * viewed by this camera, if it is present. + * + * @param layer layer to be removed + * @return the specified layer + */ + public PLayer removeLayer(final PLayer layer) { + layer.removeCamera(this); + if (layers.remove(layer)) { + invalidatePaint(); + firePropertyChange(PROPERTY_CODE_LAYERS, PROPERTY_LAYERS, null, layers); + } + return layer; + } + + /** + * Removes the element at the specified position from the list of layers viewed + * by this camera. + * + * @param index index of the layer to remove + * @return the layer previously at the specified position + * @throws IndexOutOfBoundsException if the specified index is out of range + * (index < 0 || index >= getLayerCount()) + */ + public PLayer removeLayer(final int index) { + final PLayer layer = (PLayer) layers.remove(index); + layer.removeCamera(this); + invalidatePaint(); + firePropertyChange(PROPERTY_CODE_LAYERS, PROPERTY_LAYERS, null, layers); + return layer; + } + + /** + * Return the union of the full bounds of each layer in the list of layers + * viewed by this camera, or empty bounds if the list of layers viewed by this + * camera is empty. + * + * @return the union of the full bounds of each layer in the list of layers + * viewed by this camera, or empty bounds if the list of layers viewed + * by this camera is empty + */ + public PBounds getUnionOfLayerFullBounds() { + final PBounds result = new PBounds(); + final int size = layers.size(); + for (int i = 0; i < size; i++) { + final PLayer each = (PLayer) layers.get(i); + result.add(each.getFullBoundsReference()); + } + return result; + } + + /** + * Paint this camera and then paint this camera's view through its view + * transform. + * + * @param paintContext context in which painting occurs + */ + protected void paint(final PPaintContext paintContext) { + super.paint(paintContext); + + paintContext.pushClip(getBoundsReference()); + paintContext.pushTransform(viewTransform); + + paintCameraView(paintContext); + paintDebugInfo(paintContext); + + paintContext.popTransform(viewTransform); + paintContext.popClip(getBoundsReference()); + } + + /** + * Paint all the layers in the list of layers viewed by this camera. This method + * is called after the view transform and clip have been applied to the + * specified paint context. + * + * @param paintContext context in which painting occurs + */ + protected void paintCameraView(final PPaintContext paintContext) { + final int size = layers.size(); + for (int i = 0; i < size; i++) { + final PLayer each = layers.get(i); + each.fullPaint(paintContext); + } + } + + /** + * Renders debug info onto the newly painted scene. Things like full bounds and + * bounds are painted as filled and outlines. + * + * @param paintContext context in which painting occurs + */ + protected void paintDebugInfo(final PPaintContext paintContext) { + if (PDebug.debugBounds || PDebug.debugFullBounds) { + final Graphics2D g2 = paintContext.getGraphics(); + paintContext.setRenderQuality(PPaintContext.LOW_QUALITY_RENDERING); + g2.setStroke(new BasicStroke(0)); + final ArrayList nodes = new ArrayList(); + final PBounds nodeBounds = new PBounds(); + + final Color boundsColor = Color.red; + final Color fullBoundsColor = new Color(1.0f, 0f, 0f, 0.2f); + + final int size = layers.size(); + for (int i = 0; i < size; i++) { + layers.get(i).getAllNodes(null, nodes); + } + + final Iterator i = getAllNodes(null, nodes).iterator(); + + while (i.hasNext()) { + final PNode each = i.next(); + + if (PDebug.debugBounds) { + g2.setPaint(boundsColor); + nodeBounds.setRect(each.getBoundsReference()); + + if (!nodeBounds.isEmpty()) { + each.localToGlobal(nodeBounds); + globalToLocal(nodeBounds); + if (each == this || each.isDescendentOf(this)) { + localToView(nodeBounds); + } + g2.draw(nodeBounds); + } + } + + if (PDebug.debugFullBounds) { + g2.setPaint(fullBoundsColor); + nodeBounds.setRect(each.getFullBoundsReference()); + + if (!nodeBounds.isEmpty()) { + if (each.getParent() != null) { + each.getParent().localToGlobal(nodeBounds); + } + globalToLocal(nodeBounds); + if (each == this || each.isDescendentOf(this)) { + localToView(nodeBounds); + } + g2.fill(nodeBounds); + } + } + } + } + } + + /** + * {@inheritDoc} + * + *

+ * Pushes this camera onto the specified paint context so that it can be + * accessed later by {@link PPaintContext#getCamera}. + *

+ */ + public void fullPaint(final PPaintContext paintContext) { + paintContext.pushCamera(this); + super.fullPaint(paintContext); + paintContext.popCamera(); + } + + /** + * Generate and return a PPickPath for the point x,y specified in the local + * coord system of this camera. Picking is done with a rectangle, halo specifies + * how large that rectangle will be. + * + * @param x the x coordinate of the pick path given in local coordinates + * @param y the y coordinate of the pick path given in local coordinates + * @param halo the distance from the x,y coordinate that is considered for + * inclusion in the pick path + * + * @return the picked path + */ + public PPickPath pick(final double x, final double y, final double halo) { + final PBounds b = new PBounds(new Point2D.Double(x, y), -halo, -halo); + final PPickPath result = new PPickPath(this, b); + + fullPick(result); + + // make sure this camera is pushed. + if (result.getNodeStackReference().size() == 0) { + result.pushNode(this); + result.pushTransform(getTransformReference(false)); + } + + return result; + } + + /** + * {@inheritDoc} + * + *

+ * After the direct children of this camera have been given a chance to be + * picked all of the layers in the list of layers viewed by this camera are + * given a chance to be picked. + *

+ * + * @return true if any of the layers in the list of layers viewed by this camera + * were picked + */ + protected boolean pickAfterChildren(final PPickPath pickPath) { + if (intersects(pickPath.getPickBounds())) { + pickPath.pushTransform(viewTransform); + + if (pickCameraView(pickPath)) { + return true; + } + + pickPath.popTransform(viewTransform); + return true; + } + return false; + } + + /** + * Try to pick all of the layers in the list of layers viewed by this camera. + * This method is called after the view transform has been applied to the + * specified pick path. + * + * @param pickPath pick path + * @return true if any of the layers in the list of layers viewed by this camera + * were picked + */ + protected boolean pickCameraView(final PPickPath pickPath) { + final int size = layers.size(); + for (int i = size - 1; i >= 0; i--) { + final PLayer each = (PLayer) layers.get(i); + if (each.fullPick(pickPath)) { + return true; + } + } + return false; + } + + // **************************************************************** + // View Transform - Methods for accessing the view transform. The + // view transform is applied before painting and picking the cameras + // layers. But not before painting or picking its direct children. + // + // Changing the view transform is how zooming and panning are + // accomplished. + // **************************************************************** + + /** + * Return the bounds of this camera in the view coordinate system. + * + * @return the bounds of this camera in the view coordinate system + */ + public PBounds getViewBounds() { + return (PBounds) localToView(getBounds()); + } + + /** + * Animates the camera's view so that the given bounds (in camera layer's + * coordinate system) are centered within the cameras view bounds. Use this + * method to point the camera at a given location. + * + * @param centerBounds the targetBounds + */ + public void setViewBounds(final Rectangle2D centerBounds) { + animateViewToCenterBounds(centerBounds, true, 0); + } + + /** + * Return the scale applied by the view transform to the list of layers viewed + * by this camera. + * + * @return the scale applied by the view transform to the list of layers viewed + * by this camera + */ + public double getViewScale() { + return viewTransform.getScale(); + } + + /** + * Scale the view transform applied to the list of layers viewed by this camera + * by scale about the point [0, 0]. + * + * @param scale view transform scale + */ + public void scaleView(final double scale) { + scaleViewAboutPoint(scale, 0, 0); + } + + /** + * Scale the view transform applied to the list of layers viewed by this camera + * by scale about the specified point [x, y]. + * + * @param scale view transform scale + * @param x scale about point, x coordinate + * @param y scale about point, y coordinate + */ + public void scaleViewAboutPoint(final double scale, final double x, final double y) { + viewTransform.scaleAboutPoint(scale, x, y); + applyViewConstraints(); + invalidatePaint(); + firePropertyChange(PROPERTY_CODE_VIEW_TRANSFORM, PROPERTY_VIEW_TRANSFORM, null, viewTransform); + } + + /** + * Set the scale applied by the view transform to the list of layers viewed by + * this camera to scale. + * + * @param scale view transform scale + */ + public void setViewScale(final double scale) { + scaleView(scale / getViewScale()); + } + + /** + * Translate the view transform applied to the list of layers viewed by this + * camera by [dx, dy]. + * + * @param dx translate delta x + * @param dy translate delta y + */ + public void translateView(final double dx, final double dy) { + viewTransform.translate(dx, dy); + applyViewConstraints(); + invalidatePaint(); + firePropertyChange(PROPERTY_CODE_VIEW_TRANSFORM, PROPERTY_VIEW_TRANSFORM, null, viewTransform); + } + + /** + * Offset the view transform applied to the list of layers viewed by this camera + * by [dx, dy]. This is NOT effected by the view transform's + * current scale or rotation. This is implemented by directly adding dx to the + * m02 position and dy to the m12 position in the affine transform. + * + * @param dx offset delta x + * @param dy offset delta y + */ + /* + * public void offsetView(final double dx, final double dy) { + * setViewOffset(viewTransform.getTranslateX() + dx, + * viewTransform.getTranslateY() + dy); } + */ + + /** + * Set the offset for the view transform applied to the list of layers viewed by + * this camera to [x, y]. + * + * @param x offset x + * @param y offset y + */ + public void setViewOffset(final double x, final double y) { + viewTransform.setOffset(x, y); + applyViewConstraints(); + invalidatePaint(); + firePropertyChange(PROPERTY_CODE_VIEW_TRANSFORM, PROPERTY_VIEW_TRANSFORM, null, viewTransform); + } + + /** + * Return a copy of the view transform applied to the list of layers viewed by + * this camera. + * + * @return a copy of the view transform applied to the list of layers viewed by + * this camera + */ + public PAffineTransform getViewTransform() { + return (PAffineTransform) viewTransform.clone(); + } + + /** + * Return a reference to the view transform applied to the list of layers viewed + * by this camera. + * + * @return the view transform applied to the list of layers viewed by this + * camera + */ + public PAffineTransform getViewTransformReference() { + return viewTransform; + } + + /** + * Set the view transform applied to the list of layers viewed by this camera to + * viewTransform. + * + * @param viewTransform view transform applied to the list of layers viewed by + * this camera + */ + public void setViewTransform(final AffineTransform viewTransform) { + this.viewTransform.setTransform(viewTransform); + applyViewConstraints(); + invalidatePaint(); + firePropertyChange(PROPERTY_CODE_VIEW_TRANSFORM, PROPERTY_VIEW_TRANSFORM, null, this.viewTransform); + } + + /** + * Animate the camera's view from its current transform when the activity starts + * to a new transform that centers the given bounds in the camera layer's + * coordinate system into the cameras view bounds. If the duration is 0 then the + * view will be transformed immediately, and null will be returned. Else a new + * PTransformActivity will get returned that is set to animate the camera's view + * transform to the new bounds. If shouldScale is true, then the camera will + * also scale its view so that the given bounds fit fully within the cameras + * view bounds, else the camera will maintain its original scale. + * + * @param centerBounds the bounds which the animation will pace at the + * center of the view + * @param shouldScaleToFit whether the camera should scale the view while + * animating to it + * @param duration how many milliseconds the animations should take + * + * @return the scheduled PTransformActivity + */ + public PTransformActivity animateViewToCenterBounds(final Rectangle2D centerBounds, final boolean shouldScaleToFit, + final long duration) { + final PBounds viewBounds = getViewBounds(); + final PDimension delta = viewBounds.deltaRequiredToCenter(centerBounds); + final PAffineTransform newTransform = getViewTransform(); + newTransform.translate(delta.width, delta.height); + + if (shouldScaleToFit) { + final double s = Math.min(viewBounds.getWidth() / centerBounds.getWidth(), + viewBounds.getHeight() / centerBounds.getHeight()); + if (s != Double.POSITIVE_INFINITY && s != 0) { + newTransform.scaleAboutPoint(s, centerBounds.getCenterX(), centerBounds.getCenterY()); + } + } + + return animateViewToTransform(newTransform, duration); + } + + /** + * Pan the camera's view from its current transform when the activity starts to + * a new transform so that the view bounds will contain (if possible, intersect + * if not possible) the new bounds in the camera layers coordinate system. If + * the duration is 0 then the view will be transformed immediately, and null + * will be returned. Else a new PTransformActivity will get returned that is set + * to animate the camera's view transform to the new bounds. + * + * @param panToBounds the bounds to which the view will animate to + * @param duration the duration of the animation given in milliseconds + * + * @return the scheduled PTransformActivity + */ + public PTransformActivity animateViewToPanToBounds(final Rectangle2D panToBounds, final long duration) { + final PBounds viewBounds = getViewBounds(); + final PDimension delta = viewBounds.deltaRequiredToContain(panToBounds); + + if (delta.width != 0 || delta.height != 0) { + if (duration == 0) { + translateView(-delta.width, -delta.height); + } else { + final AffineTransform at = getViewTransform(); + at.translate(-delta.width, -delta.height); + return animateViewToTransform(at, duration); + } + } + + return null; + } + + /** + * Animate the cameras view transform from its current value when the activity + * starts to the new destination transform value. + * + * @param destination the transform to which the view should be transformed into + * @param duration the duraiton in milliseconds the animation should take + * + * @return the scheduled PTransformActivity + */ + public PTransformActivity animateViewToTransform(final AffineTransform destination, final long duration) { + if (duration == 0) { + setViewTransform(destination); + return null; + } + + final PTransformActivity.Target t = new PTransformActivity.Target() { + /** {@inheritDoc} */ + public void setTransform(final AffineTransform aTransform) { + PCamera.this.setViewTransform(aTransform); + } + + /** {@inheritDoc} */ + public void getSourceMatrix(final double[] aSource) { + viewTransform.getMatrix(aSource); + } + }; + + final PTransformActivity transformActivity = new PTransformActivity(duration, PUtil.DEFAULT_ACTIVITY_STEP_RATE, + t, destination); + + final PRoot r = getRoot(); + if (r != null) { + r.getActivityScheduler().addActivity(transformActivity); + } + + return transformActivity; + } + + // **************************************************************** + // View Transform Constraints - Methods for setting and applying + // constraints to the view transform. + // **************************************************************** + + /** + * Return the constraint applied to the view. The view constraint will be one of + * {@link #VIEW_CONSTRAINT_NONE}, {@link #VIEW_CONSTRAINT_CENTER}, or + * {@link #VIEW_CONSTRAINT_CENTER}. Defaults to {@link #VIEW_CONSTRAINT_NONE}. + * + * @return the view constraint being applied to the view + */ + public int getViewConstraint() { + return viewConstraint; + } + + /** + * Set the view constraint to apply to the view to viewConstraint. + * The view constraint must be one of {@link #VIEW_CONSTRAINT_NONE}, + * {@link #VIEW_CONSTRAINT_CENTER}, or {@link #VIEW_CONSTRAINT_CENTER}. + * + * @param viewConstraint constraint to apply to the view + * @throws IllegalArgumentException if viewConstraint is not one of + * {@link #VIEW_CONSTRAINT_NONE}, + * {@link #VIEW_CONSTRAINT_CENTER}, or + * {@link #VIEW_CONSTRAINT_CENTER} + */ + public void setViewConstraint(final int viewConstraint) { + if (viewConstraint != VIEW_CONSTRAINT_NONE && viewConstraint != VIEW_CONSTRAINT_CENTER + && viewConstraint != VIEW_CONSTRAINT_ALL) { + throw new IllegalArgumentException("view constraint must be one " + + "of VIEW_CONSTRAINT_NONE, VIEW_CONSTRAINT_CENTER, or VIEW_CONSTRAINT_ALL"); + } + this.viewConstraint = viewConstraint; + applyViewConstraints(); + } + + /** + * Transforms the view so that it conforms to the given constraint. + */ + protected void applyViewConstraints() { + if (VIEW_CONSTRAINT_NONE == viewConstraint) { + return; + } + final PBounds viewBounds = getViewBounds(); + final PBounds layerBounds = (PBounds) globalToLocal(getUnionOfLayerFullBounds()); + + if (VIEW_CONSTRAINT_CENTER == viewConstraint) { + layerBounds.setRect(layerBounds.getCenterX(), layerBounds.getCenterY(), 0, 0); + } + PDimension constraintDelta = viewBounds.deltaRequiredToContain(layerBounds); + viewTransform.translate(-constraintDelta.width, -constraintDelta.height); + } + + // **************************************************************** + // Camera View Coord System Conversions - Methods to translate from + // the camera's local coord system (above the camera's view transform) to the + // camera view coord system (below the camera's view transform). When + // converting geometry from one of the canvas's layers you must go + // through the view transform. + // **************************************************************** + + /** + * Convert the point from the camera's view coordinate system to the camera's + * local coordinate system. The given point is modified by this. + * + * @param viewPoint the point to transform to the local coordinate system from + * the view's coordinate system + * @return the transformed point + */ + public Point2D viewToLocal(final Point2D viewPoint) { + return viewTransform.transform(viewPoint, viewPoint); + } + + /** + * Convert the dimension from the camera's view coordinate system to the + * camera's local coordinate system. The given dimension is modified by this. + * + * @param viewDimension the dimension to transform from the view system to the + * local coordinate system + * + * @return returns the transformed dimension + */ + public Dimension2D viewToLocal(final Dimension2D viewDimension) { + return viewTransform.transform(viewDimension, viewDimension); + } + + /** + * Convert the rectangle from the camera's view coordinate system to the + * camera's local coordinate system. The given rectangle is modified by this + * method. + * + * @param viewRectangle the rectangle to transform from view to local coordinate + * System + * @return the transformed rectangle + */ + public Rectangle2D viewToLocal(final Rectangle2D viewRectangle) { + return viewTransform.transform(viewRectangle, viewRectangle); + } + + /** + * Convert the point from the camera's local coordinate system to the camera's + * view coordinate system. The given point is modified by this method. + * + * @param localPoint point to transform from local to view coordinate system + * @return the transformed point + */ + public Point2D localToView(final Point2D localPoint) { + return viewTransform.inverseTransform(localPoint, localPoint); + } + + /** + * Convert the dimension from the camera's local coordinate system to the + * camera's view coordinate system. The given dimension is modified by this + * method. + * + * @param localDimension the dimension to transform from local to view + * coordinate systems + * @return the transformed dimension + */ + public Dimension2D localToView(final Dimension2D localDimension) { + return viewTransform.inverseTransform(localDimension, localDimension); + } + + /** + * Convert the rectangle from the camera's local coordinate system to the + * camera's view coordinate system. The given rectangle is modified by this + * method. + * + * @param localRectangle the rectangle to transform from local to view + * coordinate system + * @return the transformed rectangle + */ + public Rectangle2D localToView(final Rectangle2D localRectangle) { + return viewTransform.inverseTransform(localRectangle, localRectangle); + } + + // **************************************************************** + // Serialization - Cameras conditionally serialize their layers. + // This means that only the layer references that were unconditionally + // (using writeObject) serialized by someone else will be restored + // when the camera is unserialized. + // ****************************************************************/ + + /** + * Write this camera and all its children out to the given stream. Note that the + * cameras layers are written conditionally, so they will only get written out + * if someone else writes them unconditionally. + * + * @param out the PObjectOutputStream to which this camera should be serialized + * @throws IOException if an error occured writing to the output stream + */ + private void writeObject(final ObjectOutputStream out) throws IOException { + if (!(out instanceof PObjectOutputStream)) { + throw new RuntimeException("cannot serialize PCamera to a non PObjectOutputStream"); + } + out.defaultWriteObject(); + + final int count = getLayerCount(); + for (int i = 0; i < count; i++) { + ((PObjectOutputStream) out).writeConditionalObject(layers.get(i)); + } + + out.writeObject(Boolean.FALSE); + ((PObjectOutputStream) out).writeConditionalObject(component); + } + + /** + * Deserializes this PCamera from the ObjectInputStream. + * + * @param in the source ObjectInputStream + * @throws IOException when error occurs during read + * @throws ClassNotFoundException if the stream attempts to deserialize a + * missing class + */ + private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { + in.defaultReadObject(); + + layers = new ArrayList(); + + while (true) { + final Object each = in.readObject(); + if (each != null) { + if (each.equals(Boolean.FALSE)) { + break; + } else { + layers.add((PLayer) each); + } + } + } + + component = (PComponent) in.readObject(); + } } diff --git a/core/src/main/java/org/piccolo2d/PCanvas.java b/core/src/main/java/org/piccolo2d/PCanvas.java index 583af54c..aade9ed2 100644 --- a/core/src/main/java/org/piccolo2d/PCanvas.java +++ b/core/src/main/java/org/piccolo2d/PCanvas.java @@ -60,7 +60,6 @@ import org.piccolo2d.util.PStack; import org.piccolo2d.util.PUtil; - /** * PCanvas is a simple Swing component that can be used to embed Piccolo * into a Java Swing application. Canvases view the Piccolo scene graph through @@ -72,851 +71,830 @@ * @author Jesse Grosjean */ public class PCanvas extends JComponent implements PComponent { - /** - * Allows for future serialization code to understand versioned binary - * formats. - */ - private static final long serialVersionUID = 1L; - - /** - * The property name that identifies a change in the interacting state. - * - * @since 1.3 - */ - public static final String PROPERTY_INTERACTING = "INTERACTING_CHANGED_NOTIFICATION"; - - /** The camera though which this Canvas is viewing. */ - private PCamera camera; - - /** - * Stack of cursors used to keep track of cursors as they change through - * interactions. - */ - private final PStack cursorStack; - - /** - * Whether the canvas is considered to be interacting, will probably mean - * worse render quality. - */ - private int interacting; - /** - * The render quality to use when the scene is not being interacted or - * animated. - */ - private int normalRenderQuality; - - /** The quality to use while the scene is being animated. */ - private int animatingRenderQuality; - - /** The quality to use while the scene is being interacted with. */ - private int interactingRenderQuality; - - /** The one and only pan handler. */ - private transient PPanEventHandler panEventHandler; - - /** The one and only ZoomEventHandler. */ - private transient PZoomEventHandler zoomEventHandler; - - private boolean paintingImmediately; - - /** Used to track whether the last paint operation was during an animation. */ - private boolean animatingOnLastPaint; - - /** The mouse listener that is registered for large scale mouse events. */ - private transient MouseListener mouseListener; - - /** Remembers the key processor. */ - private transient KeyEventPostProcessor keyEventPostProcessor; - - /** The mouse wheel listeners that's registered to receive wheel events. */ - private transient MouseWheelListener mouseWheelListener; - /** - * The mouse listener that is registered to receive small scale mouse events - * (like motion). - */ - private transient MouseMotionListener mouseMotionListener; - - private static final int ALL_BUTTONS_MASK = InputEvent.BUTTON1_DOWN_MASK | InputEvent.BUTTON2_DOWN_MASK - | InputEvent.BUTTON3_DOWN_MASK; - - /** - * Construct a canvas with the basic scene graph consisting of a root, - * camera, and layer. Zooming and panning are automatically installed. - */ - public PCanvas() { - cursorStack = new PStack(); - setCamera(createDefaultCamera()); - setDefaultRenderQuality(PPaintContext.HIGH_QUALITY_RENDERING); - setAnimatingRenderQuality(PPaintContext.LOW_QUALITY_RENDERING); - setInteractingRenderQuality(PPaintContext.LOW_QUALITY_RENDERING); - setPanEventHandler(new PPanEventHandler()); - setZoomEventHandler(new PZoomEventHandler()); - setBackground(Color.WHITE); - setOpaque(true); - - addHierarchyListener(new HierarchyListener() { - public void hierarchyChanged(final HierarchyEvent e) { - if (e.getComponent() == PCanvas.this) { - if (getParent() == null) { - removeInputSources(); - } - else if (isEnabled()) { - installInputSources(); - } - } - } - }); - } - - /** - * Creates and returns a basic Scene Graph. - * - * @return a built PCamera scene - */ - protected PCamera createDefaultCamera() { - return PUtil.createBasicScenegraph(); - } - - // **************************************************************** - // Basic - Methods for accessing common piccolo 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, may be null - */ - public PPanEventHandler getPanEventHandler() { - return panEventHandler; - } - - /** - * Set the pan event handler associated with this canvas. - * - * @param handler the new zoom event handler - */ - public void setPanEventHandler(final PPanEventHandler handler) { - if (panEventHandler != null) { - removeInputEventListener(panEventHandler); - } - - panEventHandler = handler; - - if (panEventHandler != null) { - addInputEventListener(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 current zoom event handler, may be null - */ - public PZoomEventHandler getZoomEventHandler() { - return zoomEventHandler; - } - - /** - * Set the zoom event handler associated with this canvas. - * - * @param handler the new zoom event handler - */ - public void setZoomEventHandler(final PZoomEventHandler handler) { - if (zoomEventHandler != null) { - removeInputEventListener(zoomEventHandler); - } - - zoomEventHandler = handler; - - if (zoomEventHandler != null) { - addInputEventListener(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 camera through which this PCanvas views the scene - */ - 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 the camera which this PCanvas should view the scene - */ - public void setCamera(final PCamera newCamera) { - if (camera != null) { - camera.setComponent(null); - } - - camera = newCamera; - - if (camera != null) { - camera.setComponent(this); - camera.setBounds(0, 0, getWidth(), getHeight()); - } - } - - /** - * Return root for this canvas. - * - * @return the root PNode at the "bottom" of the scene - */ - public PRoot getRoot() { - return camera.getRoot(); - } - - /** - * Return layer for this canvas. - * - * @return the first layer attached to this camera - */ - public PLayer getLayer() { - return camera.getLayer(0); - } - - /** - * Add an input listener to the camera associated with this canvas. - * - * @param listener listener to register for event notifications - */ - public void addInputEventListener(final PInputEventListener listener) { - getCamera().addInputEventListener(listener); - } - - /** - * Remove an input listener to the camera associated with this canvas. - * - * @param listener listener to unregister from event notifications - */ - public void removeInputEventListener(final PInputEventListener listener) { - getCamera().removeInputEventListener(listener); - } - - // **************************************************************** - // Painting - // **************************************************************** - - /** - * Return true if this canvas has been marked as interacting, or whether - * it's root is interacting. If so the canvas will normally render at a - * lower quality that is faster. - * - * @return whether the canvas has been flagged as being interacted with - */ - public boolean getInteracting() { - return interacting > 0 || getRoot().getInteracting(); - } - - /** - * 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 whether the PCanvas is currently being animated - */ - public boolean getAnimating() { - return getRoot().getActivityScheduler().getAnimating(); - } - - /** - * Set if this canvas is interacting. If so the canvas will normally render - * at a lower quality that is faster. Also repaints the canvas if the render - * quality should change. - * - * @param isInteracting whether the PCanvas should be considered interacting - */ - public void setInteracting(final boolean isInteracting) { - final boolean wasInteracting = getInteracting(); - - if (isInteracting) { - interacting++; - } - else { - interacting--; - } - - if (!getInteracting()) { // determine next render quality and repaint if - // it's greater then the old - // interacting render quality. - int nextRenderQuality = normalRenderQuality; - if (getAnimating()) { - nextRenderQuality = animatingRenderQuality; - } - if (nextRenderQuality > interactingRenderQuality) { - repaint(); - } - } - - final boolean newInteracting = getInteracting(); - - if (wasInteracting != newInteracting) { - firePropertyChange(PROPERTY_INTERACTING, wasInteracting, newInteracting); - } - } - - /** - * Set the render quality that should be used when rendering this canvas - * when it is not interacting or animating. The default value is - * PPaintContext. HIGH_QUALITY_RENDERING. - * - * @param defaultRenderQuality supports PPaintContext.HIGH_QUALITY_RENDERING - * or PPaintContext.LOW_QUALITY_RENDERING - */ - public void setDefaultRenderQuality(final int defaultRenderQuality) { - this.normalRenderQuality = defaultRenderQuality; - 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 animatingRenderQuality supports - * PPaintContext.HIGH_QUALITY_RENDERING or - * PPaintContext.LOW_QUALITY_RENDERING - */ - public void setAnimatingRenderQuality(final int animatingRenderQuality) { - this.animatingRenderQuality = animatingRenderQuality; - if (getAnimating()) { - 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 interactingRenderQuality supports - * PPaintContext.HIGH_QUALITY_RENDERING or - * PPaintContext.LOW_QUALITY_RENDERING - */ - public void setInteractingRenderQuality(final int interactingRenderQuality) { - this.interactingRenderQuality = interactingRenderQuality; - if (getInteracting()) { - repaint(); - } - } - - /** - * Set the canvas cursor, and remember the previous cursor on the cursor - * stack. - * - * @param cursor the cursor to push onto the cursor stack - */ - public void pushCursor(final Cursor cursor) { - cursorStack.push(getCursor()); - setCursor(cursor); - } - - /** - * Pop the cursor on top of the cursorStack and set it as the canvas cursor. - */ - public void popCursor() { - if (!cursorStack.isEmpty()) { - setCursor((Cursor) cursorStack.pop()); - } - } - - // **************************************************************** - // Code to manage connection to Swing. There appears to be a bug in - // swing where it will occasionally send too many mouse pressed or mouse - // released events. Below we attempt to filter out those cases before - // they get delivered to the Piccolo framework. - // **************************************************************** - - /** - * Tracks whether button1 of the mouse is down. - */ - private boolean isButton1Pressed; - /** - * Tracks whether button2 of the mouse is down. - */ - private boolean isButton2Pressed; - /** - * Tracks whether button3 of the mouse is down. - */ - private boolean isButton3Pressed; - - /** - * Override setEnabled to install/remove canvas input sources as needed. - * - * @param enabled new enable status of the Pcanvas - */ - public void setEnabled(final boolean enabled) { - super.setEnabled(enabled); - - if (isEnabled() && getParent() != null) { - installInputSources(); - } - else { - removeInputSources(); - } - } - - /** - * This method installs mouse and key listeners on the canvas that forward - * those events to piccolo. - */ - protected void installInputSources() { - if (mouseListener == null) { - mouseListener = new MouseEventInputSource(); - addMouseListener(mouseListener); - } - - if (mouseMotionListener == null) { - mouseMotionListener = new MouseMotionInputSourceListener(); - addMouseMotionListener(mouseMotionListener); - } - - if (mouseWheelListener == null) { - mouseWheelListener = new MouseWheelInputSourceListener(); - addMouseWheelListener(mouseWheelListener); - } - - if (keyEventPostProcessor == null) { - keyEventPostProcessor = new KeyEventInputSourceListener(); - KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventPostProcessor(keyEventPostProcessor); - } - } - - /** - * This method removes mouse and key listeners on the canvas that forward - * those events to piccolo. - */ - protected void removeInputSources() { - removeMouseListener(mouseListener); - removeMouseMotionListener(mouseMotionListener); - removeMouseWheelListener(mouseWheelListener); - KeyboardFocusManager.getCurrentKeyboardFocusManager().removeKeyEventPostProcessor(keyEventPostProcessor); - - mouseListener = null; - mouseMotionListener = null; - mouseWheelListener = null; - keyEventPostProcessor = null; - } - - /** - * Sends the given input event with the given type to the current - * InputManager. - * - * @param event event to dispatch - * @param type type of event being dispatched - */ - protected void sendInputEventToInputManager(final InputEvent event, final int type) { - getRoot().getDefaultInputManager().processEventFromCamera(event, type, getCamera()); - } - - /** - * Updates the bounds of the component and updates the camera accordingly. - * - * @param x left of bounds - * @param y top of bounds - * @param width width of bounds - * @param height height of bounds - */ - public void setBounds(final int x, final int y, final int width, final int height) { - camera.setBounds(camera.getX(), camera.getY(), width, height); - super.setBounds(x, y, width, height); - } - - /** - * {@inheritDoc} - */ - public void repaint(final PBounds bounds) { - PDebug.processRepaint(); - - bounds.expandNearestIntegerDimensions(); - bounds.inset(-1, -1); - - repaint((int) bounds.x, (int) bounds.y, (int) bounds.width, (int) bounds.height); - } - - private PBounds repaintBounds = new PBounds(); - - /** - * {@inheritDoc} - */ - public void paintComponent(final Graphics g) { - PDebug.startProcessingOutput(); - - final Graphics2D g2 = (Graphics2D) g.create(); - - // support for non-opaque canvases - // see - // http://groups.google.com/group/piccolo2d-dev/browse_thread/thread/134e2792d3a54cf - if (isOpaque()) { - g2.setColor(getBackground()); - g2.fillRect(0, 0, getWidth(), getHeight()); - } - - if (getAnimating()) { - repaintBounds.add(g2.getClipBounds()); - } - - // create new paint context and set render quality to lowest common - // denominator render quality. - final PPaintContext paintContext = new PPaintContext(g2); - if (getInteracting() || getAnimating()) { - if (interactingRenderQuality < animatingRenderQuality) { - paintContext.setRenderQuality(interactingRenderQuality); - } - else { - paintContext.setRenderQuality(animatingRenderQuality); - } - } - else { - paintContext.setRenderQuality(normalRenderQuality); - } - - camera.fullPaint(paintContext); - - // if switched state from animating to not animating invalidate the - // repaint bounds so that it will be drawn with the default instead of - // animating render quality. - if (!getAnimating() && animatingOnLastPaint) { - repaint(repaintBounds); - repaintBounds.reset(); - } - - animatingOnLastPaint = getAnimating(); - - PDebug.endProcessingOutput(g2); - } - - /** - * If not painting immediately, send paint notification to RepaintManager, - * otherwise does nothing. - */ - public void paintImmediately() { - if (paintingImmediately) { - return; - } - - paintingImmediately = true; - RepaintManager.currentManager(this).paintDirtyRegions(); - paintingImmediately = false; - } - - /** - * Helper for creating a timer. It's an extension point for subclasses to - * install their own timers. - * - * @param delay the number of milliseconds to wait before invoking the - * listener - * @param listener the listener to invoke after the delay - * - * @return the created Timer - */ - public Timer createTimer(final int delay, final ActionListener listener) { - return new Timer(delay, listener); - } - - /** - * Returns the quality to use when not animating or interacting. - * - * @since 1.3 - * @return the render quality to use when not animating or interacting - */ - public int getNormalRenderQuality() { - return normalRenderQuality; - } - - /** - * Returns the quality to use when animating. - * - * @since 1.3 - * @return Returns the quality to use when animating - */ - public int getAnimatingRenderQuality() { - return animatingRenderQuality; - } - - /** - * Returns the quality to use when interacting. - * - * @since 1.3 - * @return Returns the quality to use when interacting - */ - public int getInteractingRenderQuality() { - return interactingRenderQuality; - } - - /** - * Returns the input event listeners registered to receive input events. - * - * @since 1.3 - * @return array or input event listeners - */ - public PInputEventListener[] getInputEventListeners() { - return camera.getInputEventListeners(); - } - - /** - * Prints the entire scene regardless of what the viewable area is. - * - * @param graphics Graphics context onto which to paint the scene for printing - */ - public void printAll(final Graphics graphics) { - if (!(graphics instanceof Graphics2D)) { - throw new IllegalArgumentException("Provided graphics context is not a Graphics2D object"); - } - - final Graphics2D g2 = (Graphics2D) graphics; - - final PBounds clippingRect = new PBounds(graphics.getClipBounds()); - clippingRect.expandNearestIntegerDimensions(); - - final PBounds originalCameraBounds = getCamera().getBounds(); - final PBounds layerBounds = getCamera().getUnionOfLayerFullBounds(); - getCamera().setBounds(layerBounds); - - final double clipRatio = clippingRect.getWidth() / clippingRect.getHeight(); - final double nodeRatio = ((double) getWidth()) / ((double) getHeight()); - final double scale; - if (nodeRatio <= clipRatio) { - scale = clippingRect.getHeight() / getCamera().getHeight(); - } - else { - scale = clippingRect.getWidth() / getCamera().getWidth(); - } - g2.scale(scale, scale); - g2.translate(-clippingRect.x, -clippingRect.y); - - final PPaintContext pc = new PPaintContext(g2); - pc.setRenderQuality(PPaintContext.HIGH_QUALITY_RENDERING); - getCamera().fullPaint(pc); - - getCamera().setBounds(originalCameraBounds); - } - - private final class MouseMotionInputSourceListener implements MouseMotionListener { - /** {@inheritDoc} */ - public void mouseDragged(final MouseEvent e) { - sendInputEventToInputManager(e, MouseEvent.MOUSE_DRAGGED); - } - - /** {@inheritDoc} */ - public void mouseMoved(final MouseEvent e) { - sendInputEventToInputManager(e, MouseEvent.MOUSE_MOVED); - } - } - - private final class MouseEventInputSource implements MouseListener { - /** {@inheritDoc} */ - public void mouseClicked(final MouseEvent e) { - sendInputEventToInputManager(e, MouseEvent.MOUSE_CLICKED); - } - - /** {@inheritDoc} */ - public void mouseEntered(final MouseEvent e) { - MouseEvent simulated = null; - - if (isAnyButtonDown(e)) { - simulated = buildRetypedMouseEvent(e, MouseEvent.MOUSE_DRAGGED); - } - else { - simulated = buildRetypedMouseEvent(e, MouseEvent.MOUSE_MOVED); - } - - sendInputEventToInputManager(e, MouseEvent.MOUSE_ENTERED); - sendInputEventToInputManager(simulated, simulated.getID()); - } - - /** {@inheritDoc} */ - public void mouseExited(final MouseEvent e) { - MouseEvent simulated = null; - - if (isAnyButtonDown(e)) { - simulated = buildRetypedMouseEvent(e, MouseEvent.MOUSE_DRAGGED); - } - else { - simulated = buildRetypedMouseEvent(e, MouseEvent.MOUSE_MOVED); - } - - sendInputEventToInputManager(simulated, simulated.getID()); - sendInputEventToInputManager(e, MouseEvent.MOUSE_EXITED); - } - - /** {@inheritDoc} */ - public void mousePressed(final MouseEvent rawEvent) { - requestFocus(); - - boolean shouldBalanceEvent = false; - boolean shouldSendEvent = false; - - final MouseEvent event = copyButtonsFromModifiers(rawEvent, MouseEvent.MOUSE_PRESSED); - - switch (event.getButton()) { - case MouseEvent.BUTTON1: - if (isButton1Pressed) { - shouldBalanceEvent = true; - } - isButton1Pressed = true; - shouldSendEvent = true; - break; - - case MouseEvent.BUTTON2: - if (isButton2Pressed) { - shouldBalanceEvent = true; - } - isButton2Pressed = true; - shouldSendEvent = true; - break; - - case MouseEvent.BUTTON3: - if (isButton3Pressed) { - shouldBalanceEvent = true; - } - isButton3Pressed = true; - shouldSendEvent = true; - break; - - default: - break; - } - - if (shouldBalanceEvent) { - sendRetypedMouseEventToInputManager(event, MouseEvent.MOUSE_RELEASED); - } - - if (shouldSendEvent) { - sendInputEventToInputManager(event, MouseEvent.MOUSE_PRESSED); - } - } - - /** {@inheritDoc} */ - public void mouseReleased(final MouseEvent rawEvent) { - boolean shouldBalanceEvent = false; - boolean shouldSendEvent = false; - - final MouseEvent event = copyButtonsFromModifiers(rawEvent, MouseEvent.MOUSE_RELEASED); - - switch (event.getButton()) { - case MouseEvent.BUTTON1: - if (!isButton1Pressed) { - shouldBalanceEvent = true; - } - isButton1Pressed = false; - shouldSendEvent = true; - break; - - case MouseEvent.BUTTON2: - if (!isButton2Pressed) { - shouldBalanceEvent = true; - } - isButton2Pressed = false; - shouldSendEvent = true; - break; - - case MouseEvent.BUTTON3: - if (!isButton3Pressed) { - shouldBalanceEvent = true; - } - isButton3Pressed = false; - shouldSendEvent = true; - break; - - default: - shouldBalanceEvent = false; - shouldSendEvent = false; - } - - if (shouldBalanceEvent) { - sendRetypedMouseEventToInputManager(event, MouseEvent.MOUSE_PRESSED); - } - - if (shouldSendEvent) { - sendInputEventToInputManager(event, MouseEvent.MOUSE_RELEASED); - } - } - - private MouseEvent copyButtonsFromModifiers(final MouseEvent rawEvent, final int eventType) { - if (rawEvent.getButton() != MouseEvent.NOBUTTON) { - return rawEvent; - } - - int newButton = 0; - - if (hasButtonModifier(rawEvent, InputEvent.BUTTON1_MASK)) { - newButton = MouseEvent.BUTTON1; - } - else if (hasButtonModifier(rawEvent, InputEvent.BUTTON2_MASK)) { - newButton = MouseEvent.BUTTON2; - } - else if (hasButtonModifier(rawEvent, InputEvent.BUTTON3_MASK)) { - newButton = MouseEvent.BUTTON3; - } - - return buildModifiedMouseEvent(rawEvent, eventType, newButton); - } - - private boolean hasButtonModifier(final MouseEvent event, final int buttonMask) { - return (event.getModifiers() & buttonMask) == buttonMask; - } - - public MouseEvent buildRetypedMouseEvent(final MouseEvent e, final int newType) { - return buildModifiedMouseEvent(e, newType, e.getButton()); - } - - public MouseEvent buildModifiedMouseEvent(final MouseEvent e, final int newType, final int newButton) { - return new MouseEvent((Component) e.getSource(), newType, e.getWhen(), e.getModifiers(), e.getX(), - e.getY(), e.getClickCount(), e.isPopupTrigger(), newButton); - } - - private void sendRetypedMouseEventToInputManager(final MouseEvent e, final int newType) { - final MouseEvent retypedEvent = buildRetypedMouseEvent(e, newType); - sendInputEventToInputManager(retypedEvent, newType); - } - } - - private boolean isAnyButtonDown(final MouseEvent e) { - return (e.getModifiersEx() & ALL_BUTTONS_MASK) != 0; - } - - /** - * Class responsible for sending key events to the the InputManager. - */ - private final class KeyEventInputSourceListener implements KeyEventPostProcessor { - /** {@inheritDoc} */ - public boolean postProcessKeyEvent(final KeyEvent keyEvent) { - Component owner = FocusManager.getCurrentManager().getFocusOwner(); - while (owner != null) { - if (owner == PCanvas.this) { - sendInputEventToInputManager(keyEvent, keyEvent.getID()); - return true; - } - owner = owner.getParent(); - } - return false; - } - } - - /** - * Class responsible for sending mouse events to the the InputManager. - */ - private final class MouseWheelInputSourceListener implements MouseWheelListener { - /** {@inheritDoc} */ - public void mouseWheelMoved(final MouseWheelEvent e) { - sendInputEventToInputManager(e, e.getScrollType()); - if (!e.isConsumed() && getParent() != null) { - getParent().dispatchEvent(e); - } - } - } + /** + * Allows for future serialization code to understand versioned binary formats. + */ + private static final long serialVersionUID = 1L; + + /** + * The property name that identifies a change in the interacting state. + * + * @since 1.3 + */ + public static final String PROPERTY_INTERACTING = "INTERACTING_CHANGED_NOTIFICATION"; + + /** The camera though which this Canvas is viewing. */ + private PCamera camera; + + /** + * Stack of cursors used to keep track of cursors as they change through + * interactions. + */ + private final PStack cursorStack; + + /** + * Whether the canvas is considered to be interacting, will probably mean worse + * render quality. + */ + private int interacting; + /** + * The render quality to use when the scene is not being interacted or animated. + */ + private int normalRenderQuality; + + /** The quality to use while the scene is being animated. */ + private int animatingRenderQuality; + + /** The quality to use while the scene is being interacted with. */ + private int interactingRenderQuality; + + /** The one and only pan handler. */ + private transient PPanEventHandler panEventHandler; + + /** The one and only ZoomEventHandler. */ + private transient PZoomEventHandler zoomEventHandler; + + private boolean paintingImmediately; + + /** Used to track whether the last paint operation was during an animation. */ + private boolean animatingOnLastPaint; + + /** The mouse listener that is registered for large scale mouse events. */ + private transient MouseListener mouseListener; + + /** Remembers the key processor. */ + private transient KeyEventPostProcessor keyEventPostProcessor; + + /** The mouse wheel listeners that's registered to receive wheel events. */ + private transient MouseWheelListener mouseWheelListener; + /** + * The mouse listener that is registered to receive small scale mouse events + * (like motion). + */ + private transient MouseMotionListener mouseMotionListener; + + private static final int ALL_BUTTONS_MASK = InputEvent.BUTTON1_DOWN_MASK | InputEvent.BUTTON2_DOWN_MASK + | InputEvent.BUTTON3_DOWN_MASK; + + /** + * Construct a canvas with the basic scene graph consisting of a root, camera, + * and layer. Zooming and panning are automatically installed. + */ + public PCanvas() { + cursorStack = new PStack(); + setCamera(createDefaultCamera()); + setDefaultRenderQuality(PPaintContext.HIGH_QUALITY_RENDERING); + setAnimatingRenderQuality(PPaintContext.LOW_QUALITY_RENDERING); + setInteractingRenderQuality(PPaintContext.LOW_QUALITY_RENDERING); + setPanEventHandler(new PPanEventHandler()); + setZoomEventHandler(new PZoomEventHandler()); + setBackground(Color.WHITE); + setOpaque(true); + + addHierarchyListener(new HierarchyListener() { + public void hierarchyChanged(final HierarchyEvent e) { + if (e.getComponent() == PCanvas.this) { + if (getParent() == null) { + removeInputSources(); + } else if (isEnabled()) { + installInputSources(); + } + } + } + }); + } + + /** + * Creates and returns a basic Scene Graph. + * + * @return a built PCamera scene + */ + protected PCamera createDefaultCamera() { + return PUtil.createBasicScenegraph(); + } + + // **************************************************************** + // Basic - Methods for accessing common piccolo 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, may be null + */ + public PPanEventHandler getPanEventHandler() { + return panEventHandler; + } + + /** + * Set the pan event handler associated with this canvas. + * + * @param handler the new zoom event handler + */ + public void setPanEventHandler(final PPanEventHandler handler) { + if (panEventHandler != null) { + removeInputEventListener(panEventHandler); + } + + panEventHandler = handler; + + if (panEventHandler != null) { + addInputEventListener(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 current zoom event handler, may be null + */ + public PZoomEventHandler getZoomEventHandler() { + return zoomEventHandler; + } + + /** + * Set the zoom event handler associated with this canvas. + * + * @param handler the new zoom event handler + */ + public void setZoomEventHandler(final PZoomEventHandler handler) { + if (zoomEventHandler != null) { + removeInputEventListener(zoomEventHandler); + } + + zoomEventHandler = handler; + + if (zoomEventHandler != null) { + addInputEventListener(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 camera through which this PCanvas views the scene + */ + 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 the camera which this PCanvas should view the scene + */ + public void setCamera(final PCamera newCamera) { + if (camera != null) { + camera.setComponent(null); + } + + camera = newCamera; + + if (camera != null) { + camera.setComponent(this); + camera.setBounds(0, 0, getWidth(), getHeight()); + } + } + + /** + * Return root for this canvas. + * + * @return the root PNode at the "bottom" of the scene + */ + public PRoot getRoot() { + return camera.getRoot(); + } + + /** + * Return layer for this canvas. + * + * @return the first layer attached to this camera + */ + public PLayer getLayer() { + return camera.getLayer(0); + } + + /** + * Add an input listener to the camera associated with this canvas. + * + * @param listener listener to register for event notifications + */ + public void addInputEventListener(final PInputEventListener listener) { + getCamera().addInputEventListener(listener); + } + + /** + * Remove an input listener to the camera associated with this canvas. + * + * @param listener listener to unregister from event notifications + */ + public void removeInputEventListener(final PInputEventListener listener) { + getCamera().removeInputEventListener(listener); + } + + // **************************************************************** + // Painting + // **************************************************************** + + /** + * Return true if this canvas has been marked as interacting, or whether it's + * root is interacting. If so the canvas will normally render at a lower quality + * that is faster. + * + * @return whether the canvas has been flagged as being interacted with + */ + public boolean getInteracting() { + return interacting > 0 || getRoot().getInteracting(); + } + + /** + * 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 whether the PCanvas is currently being animated + */ + public boolean getAnimating() { + return getRoot().getActivityScheduler().getAnimating(); + } + + /** + * Set if this canvas is interacting. If so the canvas will normally render at a + * lower quality that is faster. Also repaints the canvas if the render quality + * should change. + * + * @param isInteracting whether the PCanvas should be considered interacting + */ + public void setInteracting(final boolean isInteracting) { + final boolean wasInteracting = getInteracting(); + + if (isInteracting) { + interacting++; + } else { + interacting--; + } + + if (!getInteracting()) { // determine next render quality and repaint if + // it's greater then the old + // interacting render quality. + int nextRenderQuality = normalRenderQuality; + if (getAnimating()) { + nextRenderQuality = animatingRenderQuality; + } + if (nextRenderQuality > interactingRenderQuality) { + repaint(); + } + } + + final boolean newInteracting = getInteracting(); + + if (wasInteracting != newInteracting) { + firePropertyChange(PROPERTY_INTERACTING, wasInteracting, newInteracting); + } + } + + /** + * Set the render quality that should be used when rendering this canvas when it + * is not interacting or animating. The default value is PPaintContext. + * HIGH_QUALITY_RENDERING. + * + * @param defaultRenderQuality supports PPaintContext.HIGH_QUALITY_RENDERING or + * PPaintContext.LOW_QUALITY_RENDERING + */ + public void setDefaultRenderQuality(final int defaultRenderQuality) { + this.normalRenderQuality = defaultRenderQuality; + 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 animatingRenderQuality supports PPaintContext.HIGH_QUALITY_RENDERING + * or PPaintContext.LOW_QUALITY_RENDERING + */ + public void setAnimatingRenderQuality(final int animatingRenderQuality) { + this.animatingRenderQuality = animatingRenderQuality; + if (getAnimating()) { + 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 interactingRenderQuality supports PPaintContext.HIGH_QUALITY_RENDERING + * or PPaintContext.LOW_QUALITY_RENDERING + */ + public void setInteractingRenderQuality(final int interactingRenderQuality) { + this.interactingRenderQuality = interactingRenderQuality; + if (getInteracting()) { + repaint(); + } + } + + /** + * Set the canvas cursor, and remember the previous cursor on the cursor stack. + * + * @param cursor the cursor to push onto the cursor stack + */ + public void pushCursor(final Cursor cursor) { + cursorStack.push(getCursor()); + setCursor(cursor); + } + + /** + * Pop the cursor on top of the cursorStack and set it as the canvas cursor. + */ + public void popCursor() { + if (!cursorStack.isEmpty()) { + setCursor((Cursor) cursorStack.pop()); + } + } + + // **************************************************************** + // Code to manage connection to Swing. There appears to be a bug in + // swing where it will occasionally send too many mouse pressed or mouse + // released events. Below we attempt to filter out those cases before + // they get delivered to the Piccolo framework. + // **************************************************************** + + /** + * Tracks whether button1 of the mouse is down. + */ + private boolean isButton1Pressed; + /** + * Tracks whether button2 of the mouse is down. + */ + private boolean isButton2Pressed; + /** + * Tracks whether button3 of the mouse is down. + */ + private boolean isButton3Pressed; + + /** + * Override setEnabled to install/remove canvas input sources as needed. + * + * @param enabled new enable status of the Pcanvas + */ + public void setEnabled(final boolean enabled) { + super.setEnabled(enabled); + + if (isEnabled() && getParent() != null) { + installInputSources(); + } else { + removeInputSources(); + } + } + + /** + * This method installs mouse and key listeners on the canvas that forward those + * events to piccolo. + */ + protected void installInputSources() { + if (mouseListener == null) { + mouseListener = new MouseEventInputSource(); + addMouseListener(mouseListener); + } + + if (mouseMotionListener == null) { + mouseMotionListener = new MouseMotionInputSourceListener(); + addMouseMotionListener(mouseMotionListener); + } + + if (mouseWheelListener == null) { + mouseWheelListener = new MouseWheelInputSourceListener(); + addMouseWheelListener(mouseWheelListener); + } + + if (keyEventPostProcessor == null) { + keyEventPostProcessor = new KeyEventInputSourceListener(); + KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventPostProcessor(keyEventPostProcessor); + } + } + + /** + * This method removes mouse and key listeners on the canvas that forward those + * events to piccolo. + */ + protected void removeInputSources() { + removeMouseListener(mouseListener); + removeMouseMotionListener(mouseMotionListener); + removeMouseWheelListener(mouseWheelListener); + KeyboardFocusManager.getCurrentKeyboardFocusManager().removeKeyEventPostProcessor(keyEventPostProcessor); + + mouseListener = null; + mouseMotionListener = null; + mouseWheelListener = null; + keyEventPostProcessor = null; + } + + /** + * Sends the given input event with the given type to the current InputManager. + * + * @param event event to dispatch + * @param type type of event being dispatched + */ + protected void sendInputEventToInputManager(final InputEvent event, final int type) { + getRoot().getDefaultInputManager().processEventFromCamera(event, type, getCamera()); + } + + /** + * Updates the bounds of the component and updates the camera accordingly. + * + * @param x left of bounds + * @param y top of bounds + * @param width width of bounds + * @param height height of bounds + */ + public void setBounds(final int x, final int y, final int width, final int height) { + camera.setBounds(camera.getX(), camera.getY(), width, height); + super.setBounds(x, y, width, height); + } + + /** + * {@inheritDoc} + */ + public void repaint(final PBounds bounds) { + PDebug.processRepaint(); + + bounds.expandNearestIntegerDimensions(); + bounds.inset(-1, -1); + + repaint((int) bounds.x, (int) bounds.y, (int) bounds.width, (int) bounds.height); + } + + private PBounds repaintBounds = new PBounds(); + + /** + * {@inheritDoc} + */ + public void paintComponent(final Graphics g) { + PDebug.startProcessingOutput(); + + final Graphics2D g2 = (Graphics2D) g.create(); + + // support for non-opaque canvases + // see + // http://groups.google.com/group/piccolo2d-dev/browse_thread/thread/134e2792d3a54cf + if (isOpaque()) { + g2.setColor(getBackground()); + g2.fillRect(0, 0, getWidth(), getHeight()); + } + + if (getAnimating()) { + repaintBounds.add(g2.getClipBounds()); + } + + // create new paint context and set render quality to lowest common + // denominator render quality. + final PPaintContext paintContext = new PPaintContext(g2); + if (getInteracting() || getAnimating()) { + if (interactingRenderQuality < animatingRenderQuality) { + paintContext.setRenderQuality(interactingRenderQuality); + } else { + paintContext.setRenderQuality(animatingRenderQuality); + } + } else { + paintContext.setRenderQuality(normalRenderQuality); + } + + camera.fullPaint(paintContext); + + // if switched state from animating to not animating invalidate the + // repaint bounds so that it will be drawn with the default instead of + // animating render quality. + if (!getAnimating() && animatingOnLastPaint) { + repaint(repaintBounds); + repaintBounds.reset(); + } + + animatingOnLastPaint = getAnimating(); + + PDebug.endProcessingOutput(g2); + } + + /** + * If not painting immediately, send paint notification to RepaintManager, + * otherwise does nothing. + */ + public void paintImmediately() { + if (paintingImmediately) { + return; + } + + paintingImmediately = true; + RepaintManager.currentManager(this).paintDirtyRegions(); + paintingImmediately = false; + } + + /** + * Helper for creating a timer. It's an extension point for subclasses to + * install their own timers. + * + * @param delay the number of milliseconds to wait before invoking the + * listener + * @param listener the listener to invoke after the delay + * + * @return the created Timer + */ + public Timer createTimer(final int delay, final ActionListener listener) { + return new Timer(delay, listener); + } + + /** + * Returns the quality to use when not animating or interacting. + * + * @since 1.3 + * @return the render quality to use when not animating or interacting + */ + public int getNormalRenderQuality() { + return normalRenderQuality; + } + + /** + * Returns the quality to use when animating. + * + * @since 1.3 + * @return Returns the quality to use when animating + */ + public int getAnimatingRenderQuality() { + return animatingRenderQuality; + } + + /** + * Returns the quality to use when interacting. + * + * @since 1.3 + * @return Returns the quality to use when interacting + */ + public int getInteractingRenderQuality() { + return interactingRenderQuality; + } + + /** + * Returns the input event listeners registered to receive input events. + * + * @since 1.3 + * @return array or input event listeners + */ + public PInputEventListener[] getInputEventListeners() { + return camera.getInputEventListeners(); + } + + /** + * Prints the entire scene regardless of what the viewable area is. + * + * @param graphics Graphics context onto which to paint the scene for printing + */ + public void printAll(final Graphics graphics) { + if (!(graphics instanceof Graphics2D)) { + throw new IllegalArgumentException("Provided graphics context is not a Graphics2D object"); + } + + final Graphics2D g2 = (Graphics2D) graphics; + + final PBounds clippingRect = new PBounds(graphics.getClipBounds()); + clippingRect.expandNearestIntegerDimensions(); + + final PBounds originalCameraBounds = getCamera().getBounds(); + final PBounds layerBounds = getCamera().getUnionOfLayerFullBounds(); + getCamera().setBounds(layerBounds); + + final double clipRatio = clippingRect.getWidth() / clippingRect.getHeight(); + final double nodeRatio = ((double) getWidth()) / ((double) getHeight()); + final double scale; + if (nodeRatio <= clipRatio) { + scale = clippingRect.getHeight() / getCamera().getHeight(); + } else { + scale = clippingRect.getWidth() / getCamera().getWidth(); + } + g2.scale(scale, scale); + g2.translate(-clippingRect.x, -clippingRect.y); + + final PPaintContext pc = new PPaintContext(g2); + pc.setRenderQuality(PPaintContext.HIGH_QUALITY_RENDERING); + getCamera().fullPaint(pc); + + getCamera().setBounds(originalCameraBounds); + } + + private final class MouseMotionInputSourceListener implements MouseMotionListener { + /** {@inheritDoc} */ + public void mouseDragged(final MouseEvent e) { + sendInputEventToInputManager(e, MouseEvent.MOUSE_DRAGGED); + } + + /** {@inheritDoc} */ + public void mouseMoved(final MouseEvent e) { + sendInputEventToInputManager(e, MouseEvent.MOUSE_MOVED); + } + } + + private final class MouseEventInputSource implements MouseListener { + /** {@inheritDoc} */ + public void mouseClicked(final MouseEvent e) { + sendInputEventToInputManager(e, MouseEvent.MOUSE_CLICKED); + } + + /** {@inheritDoc} */ + public void mouseEntered(final MouseEvent e) { + MouseEvent simulated = null; + + if (isAnyButtonDown(e)) { + simulated = buildRetypedMouseEvent(e, MouseEvent.MOUSE_DRAGGED); + } else { + simulated = buildRetypedMouseEvent(e, MouseEvent.MOUSE_MOVED); + } + + sendInputEventToInputManager(e, MouseEvent.MOUSE_ENTERED); + sendInputEventToInputManager(simulated, simulated.getID()); + } + + /** {@inheritDoc} */ + public void mouseExited(final MouseEvent e) { + MouseEvent simulated = null; + + if (isAnyButtonDown(e)) { + simulated = buildRetypedMouseEvent(e, MouseEvent.MOUSE_DRAGGED); + } else { + simulated = buildRetypedMouseEvent(e, MouseEvent.MOUSE_MOVED); + } + + sendInputEventToInputManager(simulated, simulated.getID()); + sendInputEventToInputManager(e, MouseEvent.MOUSE_EXITED); + } + + /** {@inheritDoc} */ + public void mousePressed(final MouseEvent rawEvent) { + requestFocus(); + + boolean shouldBalanceEvent = false; + boolean shouldSendEvent = false; + + final MouseEvent event = copyButtonsFromModifiers(rawEvent, MouseEvent.MOUSE_PRESSED); + + switch (event.getButton()) { + case MouseEvent.BUTTON1: + if (isButton1Pressed) { + shouldBalanceEvent = true; + } + isButton1Pressed = true; + shouldSendEvent = true; + break; + + case MouseEvent.BUTTON2: + if (isButton2Pressed) { + shouldBalanceEvent = true; + } + isButton2Pressed = true; + shouldSendEvent = true; + break; + + case MouseEvent.BUTTON3: + if (isButton3Pressed) { + shouldBalanceEvent = true; + } + isButton3Pressed = true; + shouldSendEvent = true; + break; + + default: + break; + } + + if (shouldBalanceEvent) { + sendRetypedMouseEventToInputManager(event, MouseEvent.MOUSE_RELEASED); + } + + if (shouldSendEvent) { + sendInputEventToInputManager(event, MouseEvent.MOUSE_PRESSED); + } + } + + /** {@inheritDoc} */ + public void mouseReleased(final MouseEvent rawEvent) { + boolean shouldBalanceEvent = false; + boolean shouldSendEvent = false; + + final MouseEvent event = copyButtonsFromModifiers(rawEvent, MouseEvent.MOUSE_RELEASED); + + switch (event.getButton()) { + case MouseEvent.BUTTON1: + if (!isButton1Pressed) { + shouldBalanceEvent = true; + } + isButton1Pressed = false; + shouldSendEvent = true; + break; + + case MouseEvent.BUTTON2: + if (!isButton2Pressed) { + shouldBalanceEvent = true; + } + isButton2Pressed = false; + shouldSendEvent = true; + break; + + case MouseEvent.BUTTON3: + if (!isButton3Pressed) { + shouldBalanceEvent = true; + } + isButton3Pressed = false; + shouldSendEvent = true; + break; + + default: + shouldBalanceEvent = false; + shouldSendEvent = false; + } + + if (shouldBalanceEvent) { + sendRetypedMouseEventToInputManager(event, MouseEvent.MOUSE_PRESSED); + } + + if (shouldSendEvent) { + sendInputEventToInputManager(event, MouseEvent.MOUSE_RELEASED); + } + } + + private MouseEvent copyButtonsFromModifiers(final MouseEvent rawEvent, final int eventType) { + if (rawEvent.getButton() != MouseEvent.NOBUTTON) { + return rawEvent; + } + + int newButton = 0; + + if (hasButtonModifier(rawEvent, InputEvent.BUTTON1_DOWN_MASK)) { + newButton = MouseEvent.BUTTON1; + } else if (hasButtonModifier(rawEvent, InputEvent.BUTTON2_DOWN_MASK)) { + newButton = MouseEvent.BUTTON2; + } else if (hasButtonModifier(rawEvent, InputEvent.BUTTON3_DOWN_MASK)) { + newButton = MouseEvent.BUTTON3; + } + + return buildModifiedMouseEvent(rawEvent, eventType, newButton); + } + + private boolean hasButtonModifier(final MouseEvent event, final int buttonMask) { + return (event.getModifiersEx() & buttonMask) == buttonMask; + } + + public MouseEvent buildRetypedMouseEvent(final MouseEvent e, final int newType) { + return buildModifiedMouseEvent(e, newType, e.getButton()); + } + + public MouseEvent buildModifiedMouseEvent(final MouseEvent e, final int newType, final int newButton) { + return new MouseEvent((Component) e.getSource(), newType, e.getWhen(), e.getModifiersEx(), e.getX(), + e.getY(), e.getClickCount(), e.isPopupTrigger(), newButton); + } + + private void sendRetypedMouseEventToInputManager(final MouseEvent e, final int newType) { + final MouseEvent retypedEvent = buildRetypedMouseEvent(e, newType); + sendInputEventToInputManager(retypedEvent, newType); + } + } + + private boolean isAnyButtonDown(final MouseEvent e) { + return (e.getModifiersEx() & ALL_BUTTONS_MASK) != 0; + } + + /** + * Class responsible for sending key events to the the InputManager. + */ + private final class KeyEventInputSourceListener implements KeyEventPostProcessor { + /** {@inheritDoc} */ + public boolean postProcessKeyEvent(final KeyEvent keyEvent) { + Component owner = FocusManager.getCurrentManager().getFocusOwner(); + while (owner != null) { + if (owner == PCanvas.this) { + sendInputEventToInputManager(keyEvent, keyEvent.getID()); + return true; + } + owner = owner.getParent(); + } + return false; + } + } + + /** + * Class responsible for sending mouse events to the the InputManager. + */ + private final class MouseWheelInputSourceListener implements MouseWheelListener { + /** {@inheritDoc} */ + public void mouseWheelMoved(final MouseWheelEvent e) { + sendInputEventToInputManager(e, e.getScrollType()); + if (!e.isConsumed() && getParent() != null) { + getParent().dispatchEvent(e); + } + } + } } diff --git a/core/src/main/java/org/piccolo2d/PComponent.java b/core/src/main/java/org/piccolo2d/PComponent.java index 7f14f0ff..922a28ef 100644 --- a/core/src/main/java/org/piccolo2d/PComponent.java +++ b/core/src/main/java/org/piccolo2d/PComponent.java @@ -32,7 +32,6 @@ import org.piccolo2d.util.PBounds; - /** * Interface that a component needs to implement if it wants to act as a Piccolo * canvas. @@ -42,37 +41,36 @@ */ public interface PComponent { - /** - * Called to notify PComponent that given bounds need repainting. - * - * @param bounds bounds needing repaint - */ - void repaint(PBounds bounds); + /** + * Called to notify PComponent that given bounds need repainting. + * + * @param bounds bounds needing repaint + */ + void repaint(PBounds bounds); - /** - * Sends a repaint notification the repaint manager if PComponent is not - * already painting immediately. - */ - void paintImmediately(); + /** + * Sends a repaint notification the repaint manager if PComponent is not already + * painting immediately. + */ + void paintImmediately(); - /** - * Pushes the given cursor onto the cursor stack and sets the current cursor - * to the one provided. - * - * @param cursor The cursor to set as the current one and push - */ - void pushCursor(Cursor cursor); + /** + * Pushes the given cursor onto the cursor stack and sets the current cursor to + * the one provided. + * + * @param cursor The cursor to set as the current one and push + */ + void pushCursor(Cursor cursor); - /** - * Pops the topmost cursor from the stack and sets it as the current one. - */ - void popCursor(); + /** + * Pops the topmost cursor from the stack and sets it as the current one. + */ + void popCursor(); - /** - * Sets whether the component is currently being interacted with. - * - * @param interacting whether the component is currently being interacted - * with - */ - void setInteracting(boolean interacting); + /** + * Sets whether the component is currently being interacted with. + * + * @param interacting whether the component is currently being interacted with + */ + void setInteracting(boolean interacting); } diff --git a/core/src/main/java/org/piccolo2d/PFrame.java b/core/src/main/java/org/piccolo2d/PFrame.java new file mode 100644 index 00000000..d1a527cd --- /dev/null +++ b/core/src/main/java/org/piccolo2d/PFrame.java @@ -0,0 +1,323 @@ +/* + * Copyright (c) 2008-2019, 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; + +import java.awt.Dimension; +import java.awt.DisplayMode; +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.util.ArrayList; +import java.util.Collection; +import java.util.EventListener; +import java.util.Iterator; + +import javax.swing.JFrame; +import javax.swing.SwingUtilities; + +/** + * PFrame is meant to be subclassed by applications that just need a + * PCanvas in a JFrame. It also includes full screen mode functionality when run + * in JDK 1.4. These subclasses should override the initialize method and start + * adding their own code there. Look in the examples package to see lots of uses + * of PFrame. + * + * @version 1.0 + * @author Jesse Grosjean + */ +public class PFrame extends JFrame { + private static final Dimension DEFAULT_FRAME_DIMENSION = new Dimension(400, 400); + + private static final Point DEFAULT_FRAME_POSITION = new Point(100, 100); + + /** Used to allow versioned binary streams for serializations. */ + private static final long serialVersionUID = 1L; + + /** Canvas being displayed on this PFrame. */ + private PCanvas canvas; + + /** The graphics device onto which the PFrame is being displayed. */ + private final GraphicsDevice graphicsDevice; + + /** Listener that listens for escape key. */ + private transient EventListener escapeFullScreenModeListener; + + /** + * Creates a PFrame with no title, not full screen, and with the default canvas. + */ + public PFrame() { + this("", false, null); + } + + /** + * Creates a PFrame with the given title and with the default canvas. + * + * @param title title to display at the top of the frame + * @param fullScreenMode whether to display a full screen frame or not + * @param canvas to embed in the frame + */ + public PFrame(final String title, final boolean fullScreenMode, final PCanvas canvas) { + this(title, GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice(), fullScreenMode, canvas); + } + + /** + * Creates a PFrame with the given title and with the default canvas being + * displayed on the provided device. + * + * @param title title to display at the top of the frame + * @param device device onto which PFrame is to be displayed + * @param fullScreen whether to display a full screen frame or not + * @param canvas to embed in the frame, may be null. If so, it'll create a + * default PCanvas + */ + public PFrame(final String title, final GraphicsDevice device, final boolean fullScreen, final PCanvas canvas) { + super(title, device.getDefaultConfiguration()); + + graphicsDevice = device; + + setBackground(null); + setBounds(getDefaultFrameBounds()); + + try { + setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + } catch (final SecurityException e) { + // expected from Applets + System.out.println("Ignoring security exception. Assuming Applet Context."); + } + + if (canvas == null) { + this.canvas = new PCanvas(); + } else { + this.canvas = canvas; + } + + setContentPane(this.canvas); + validate(); + setFullScreenMode(fullScreen); + this.canvas.requestFocus(); + beforeInitialize(); + + // Manipulation of Piccolo's scene graph should be done from Swings + // event dispatch thread since Piccolo2D is not thread safe. This code + // calls initialize() from that thread once the PFrame is initialized, + // so you are safe to start working with Piccolo2D in the initialize() + // method. + SwingUtilities.invokeLater(new Runnable() { + public void run() { + PFrame.this.initialize(); + repaint(); + } + }); + } + + /** + * Returns the canvas being displayed on this frame. + * + * @return canvas being displayed on this frame + */ + public PCanvas getCanvas() { + return canvas; + } + + /** + * Returns the default frame bounds. + * + * @return default frame bounds + */ + public Rectangle getDefaultFrameBounds() { + return new Rectangle(DEFAULT_FRAME_POSITION, DEFAULT_FRAME_DIMENSION); + } + + /** + * Returns whether the frame is currently in full screen mode. + * + * @return whether the frame is currently in full screen mode + */ + public boolean isFullScreenMode() { + return graphicsDevice.getFullScreenWindow() != null; + } + + /** + * Switches full screen state. + * + * @param fullScreenMode whether to place the frame in full screen mode or not. + */ + public void setFullScreenMode(final boolean fullScreenMode) { + if (fullScreenMode != isFullScreenMode() || !isVisible()) { + if (fullScreenMode) { + switchToFullScreenMode(); + } else { + switchToWindowedMode(); + } + } + } + + private void switchToFullScreenMode() { + addEscapeFullScreenModeListener(); + + if (isDisplayable()) { + dispose(); + } + + setUndecorated(true); + setResizable(false); + graphicsDevice.setFullScreenWindow(this); + + if (graphicsDevice.isDisplayChangeSupported()) { + chooseBestDisplayMode(graphicsDevice); + } + validate(); + } + + private void switchToWindowedMode() { + removeEscapeFullScreenModeListener(); + + if (isDisplayable()) { + dispose(); + } + + setUndecorated(false); + setResizable(true); + graphicsDevice.setFullScreenWindow(null); + validate(); + setVisible(true); + } + + /** + * Sets the display mode to the best device mode that can be determined. + * + * Used in full screen mode. + * + * @param device The graphics device being controlled. + */ + protected void chooseBestDisplayMode(final GraphicsDevice device) { + final DisplayMode best = getBestDisplayMode(device); + if (best != null) { + device.setDisplayMode(best); + } + } + + /** + * Finds the best display mode the graphics device supports. Based on the + * preferred modes. + * + * @param device the device being inspected + * + * @return best display mode the given device supports + */ + protected DisplayMode getBestDisplayMode(final GraphicsDevice device) { + final Iterator itr = getPreferredDisplayModes(device).iterator(); + while (itr.hasNext()) { + final DisplayMode each = itr.next(); + final DisplayMode[] modes = device.getDisplayModes(); + for (int i = 0; i < modes.length; i++) { + if (modes[i].getWidth() == each.getWidth() && modes[i].getHeight() == each.getHeight() + && modes[i].getBitDepth() == each.getBitDepth()) { + return each; + } + } + } + + return null; + } + + /** + * By default return the current display mode. Subclasses may override this + * method to return other modes in the collection. + * + * @param device the device being inspected + * @return preferred display mode + */ + protected Collection getPreferredDisplayModes(final GraphicsDevice device) { + final Collection result = new ArrayList(); + + result.add(device.getDisplayMode()); + /* + * result.add(new DisplayMode(640, 480, 32, 0)); result.add(new DisplayMode(640, + * 480, 16, 0)); result.add(new DisplayMode(640, 480, 8, 0)); + */ + + return result; + } + + /** + * This method adds a key listener that will take this PFrame out of full screen + * mode when the escape key is pressed. This is called for you automatically + * when the frame enters full screen mode. + */ + public void addEscapeFullScreenModeListener() { + removeEscapeFullScreenModeListener(); + escapeFullScreenModeListener = new KeyAdapter() { + public void keyPressed(final KeyEvent aEvent) { + if (aEvent.getKeyCode() == KeyEvent.VK_ESCAPE) { + setFullScreenMode(false); + } + } + }; + canvas.addKeyListener((KeyListener) escapeFullScreenModeListener); + } + + /** + * This method removes the escape full screen mode key listener. It will be + * called for you automatically when full screen mode exits, but the method has + * been made public for applications that wish to use other methods for exiting + * full screen mode. + */ + public void removeEscapeFullScreenModeListener() { + if (escapeFullScreenModeListener != null) { + canvas.removeKeyListener((KeyListener) escapeFullScreenModeListener); + escapeFullScreenModeListener = null; + } + } + + // **************************************************************** + // Initialize + // **************************************************************** + + /** + * This method will be called before the initialize() method and will be called + * on the thread that is constructing this object. + */ + public void beforeInitialize() { + } + + /** + * Subclasses should override this method and add their Piccolo2D initialization + * code there. This method will be called on the swing event dispatch thread. + * Note that the constructors of PFrame subclasses may not be complete when this + * method is called. If you need to initialize some things in your class before + * this method is called place that code in beforeInitialize(); + */ + public void initialize() { + } +} diff --git a/core/src/main/java/org/piccolo2d/PInputManager.java b/core/src/main/java/org/piccolo2d/PInputManager.java index d56159cc..23ec4682 100644 --- a/core/src/main/java/org/piccolo2d/PInputManager.java +++ b/core/src/main/java/org/piccolo2d/PInputManager.java @@ -40,7 +40,6 @@ import org.piccolo2d.event.PInputEventListener; import org.piccolo2d.util.PPickPath; - /** * PInputManager is responsible for dispatching PInputEvents to node's * event listeners. Events are dispatched from PRoot's processInputs method. @@ -53,329 +52,327 @@ */ public class PInputManager extends PBasicInputEventHandler implements PRoot.InputSource { - /** Records the last known mouse position on the canvas. */ - private final Point2D lastCanvasPosition; - - /** Records the current known mouse position on the canvas. */ - private final Point2D currentCanvasPosition; - - /** The next InputEvent that needs to be processed. */ - private InputEvent nextInput; - - /** The type of the next InputEvent that needs to be processed. */ - private int nextType; - - /** The Input Source the next event to process came from. */ - private PCamera nextInputSource; - - /** The current mouse focus. */ - private PPickPath mouseFocus; - - /** The previous mouse focus. */ - private PPickPath previousMouseFocus; - - /** Tracks where the mouse is right now on the canvas. */ - private PPickPath mouseOver; - - /** Tracks the previous location of the mouse on the canvas. */ - private PPickPath previousMouseOver; - - /** Tracks the input event listener that should receive keyboard events. */ - private PInputEventListener keyboardFocus; - - /** Tracks the number mouse buttons currently pressed. */ - private int buttonsPressed; - - /** - * Creates a PInputManager and sets positions (last, current) to the origin - * (0,0). - */ - public PInputManager() { - lastCanvasPosition = new Point2D.Double(); - currentCanvasPosition = new Point2D.Double(); - } - - /** - * Return the node that currently has the keyboard focus. This node receives - * the key events. - * - * @return the current keyboard focus - */ - public PInputEventListener getKeyboardFocus() { - return keyboardFocus; - } - - /** - * Set the node that should receive key events. - * - * @param eventHandler sets the keyboard event focus, may be null - */ - public void setKeyboardFocus(final PInputEventListener eventHandler) { - final PInputEvent focusEvent = new PInputEvent(this, null); - - if (keyboardFocus != null) { - dispatchEventToListener(focusEvent, FocusEvent.FOCUS_LOST, keyboardFocus); - } - - keyboardFocus = eventHandler; - - if (keyboardFocus != null) { - dispatchEventToListener(focusEvent, FocusEvent.FOCUS_GAINED, keyboardFocus); - } - } - - /** - * Return the current Pick Path under the mouse focus. This will return the - * path that received the current mouse pressed event, or null if the mouse - * is not pressed. The mouse focus gets mouse dragged events even what the - * mouse is not over the mouse focus. - * - * @return the current Pick Path under the mouse focus - */ - public PPickPath getMouseFocus() { - return mouseFocus; - } - - /** - * Sets the current Pick Path under the mouse focus. The mouse focus gets - * mouse dragged events even when the mouse is not over the mouse focus. - * - * @param path the new mouse focus - */ - public void setMouseFocus(final PPickPath path) { - previousMouseFocus = mouseFocus; - mouseFocus = path; - } - - /** - * Return the node the the mouse is currently over. - * - * @return the path over which the mouse currently is - */ - public PPickPath getMouseOver() { - return mouseOver; - } - - /** - * Records the path which is directly below the mouse. - * - * @param path path over which the mouse has been moved - */ - public void setMouseOver(final PPickPath path) { - mouseOver = path; - } - - /** - * Returns the position on the Canvas of the last event. - * - * @return position of last canvas event - */ - public Point2D getLastCanvasPosition() { - return lastCanvasPosition; - } - - /** - * Returns the position of the current canvas event. - * - * @return position of current canvas event - */ - public Point2D getCurrentCanvasPosition() { - return currentCanvasPosition; - } - - // **************************************************************** - // Event Handling - Methods for handling events - // - // The dispatch manager updates the focus nodes based on the - // incoming events, and dispatches those events to the appropriate - // focus nodes. - // **************************************************************** - - /** {@inheritDoc} */ - public void keyPressed(final PInputEvent event) { - dispatchEventToListener(event, KeyEvent.KEY_PRESSED, keyboardFocus); - } - - /** {@inheritDoc} */ - public void keyReleased(final PInputEvent event) { - dispatchEventToListener(event, KeyEvent.KEY_RELEASED, keyboardFocus); - } - - /** {@inheritDoc} */ - public void keyTyped(final PInputEvent event) { - dispatchEventToListener(event, KeyEvent.KEY_TYPED, keyboardFocus); - } - - /** {@inheritDoc} */ - public void mouseClicked(final PInputEvent event) { - dispatchEventToListener(event, MouseEvent.MOUSE_CLICKED, previousMouseFocus); - } - - /** {@inheritDoc} */ - public void mouseWheelRotated(final PInputEvent event) { - setMouseFocus(getMouseOver()); - dispatchEventToListener(event, MouseWheelEvent.WHEEL_UNIT_SCROLL, mouseOver); - } - - /** {@inheritDoc} */ - public void mouseWheelRotatedByBlock(final PInputEvent event) { - setMouseFocus(getMouseOver()); - dispatchEventToListener(event, MouseWheelEvent.WHEEL_BLOCK_SCROLL, mouseOver); - } - - /** {@inheritDoc} */ - public void mouseDragged(final PInputEvent event) { - checkForMouseEnteredAndExited(event); - dispatchEventToListener(event, MouseEvent.MOUSE_DRAGGED, mouseFocus); - } - - /** {@inheritDoc} */ - public void mouseEntered(final PInputEvent event) { - dispatchEventToListener(event, MouseEvent.MOUSE_ENTERED, mouseOver); - } - - /** {@inheritDoc} */ - public void mouseExited(final PInputEvent event) { - dispatchEventToListener(event, MouseEvent.MOUSE_EXITED, previousMouseOver); - } - - /** {@inheritDoc} */ - public void mouseMoved(final PInputEvent event) { - checkForMouseEnteredAndExited(event); - dispatchEventToListener(event, MouseEvent.MOUSE_MOVED, mouseOver); - } - - /** {@inheritDoc} */ - public void mousePressed(final PInputEvent event) { - if (buttonsPressed == 0) { - setMouseFocus(getMouseOver()); - } - buttonsPressed++; - dispatchEventToListener(event, MouseEvent.MOUSE_PRESSED, mouseFocus); - if (buttonsPressed < 1 || buttonsPressed > 3) { - System.err.println("invalid pressedCount on mouse pressed: " + buttonsPressed); - } - } - - /** {@inheritDoc} */ - public void mouseReleased(final PInputEvent event) { - buttonsPressed--; - checkForMouseEnteredAndExited(event); - dispatchEventToListener(event, MouseEvent.MOUSE_RELEASED, mouseFocus); - if (buttonsPressed == 0) { - setMouseFocus(null); - } - if (buttonsPressed < 0 || buttonsPressed > 2) { - System.err.println("invalid pressedCount on mouse released: " + buttonsPressed); - } - } - - /** - * Fires events whenever the mouse moves from PNode to PNode. - * - * @param event to check to see if the top node has changed. - */ - protected void checkForMouseEnteredAndExited(final PInputEvent event) { - final PNode currentNode = getPickedNode(mouseOver); - final PNode previousNode = getPickedNode(previousMouseOver); - - if (currentNode != previousNode) { - dispatchEventToListener(event, MouseEvent.MOUSE_EXITED, previousMouseOver); - dispatchEventToListener(event, MouseEvent.MOUSE_ENTERED, mouseOver); - previousMouseOver = mouseOver; - } - } - - /** - * Returns picked node on pickPath if pickPath is not null, or null. - * - * @param pickPath from which to extract picked node - * - * @return the picked node or null if pickPath is null - */ - private PNode getPickedNode(final PPickPath pickPath) { - if (pickPath == null) { - return null; - } - else { - return pickPath.getPickedNode(); - } - } - - // **************************************************************** - // Event Dispatch. - // **************************************************************** - /** {@inheritDoc} */ - public void processInput() { - if (nextInput == null) { - return; - } - - final PInputEvent e = new PInputEvent(this, nextInput); - - Point2D newCurrentCanvasPosition = null; - Point2D newLastCanvasPosition = null; - - if (e.isMouseEvent()) { - if (e.isMouseEnteredOrMouseExited()) { - final PPickPath aPickPath = nextInputSource.pick(((MouseEvent) nextInput).getX(), - ((MouseEvent) nextInput).getY(), 1); - setMouseOver(aPickPath); - previousMouseOver = aPickPath; - newCurrentCanvasPosition = (Point2D) currentCanvasPosition.clone(); - newLastCanvasPosition = (Point2D) lastCanvasPosition.clone(); - } - else { - lastCanvasPosition.setLocation(currentCanvasPosition); - currentCanvasPosition.setLocation(((MouseEvent) nextInput).getX(), ((MouseEvent) nextInput).getY()); - final PPickPath aPickPath = nextInputSource.pick(currentCanvasPosition.getX(), currentCanvasPosition - .getY(), 1); - setMouseOver(aPickPath); - } - } - - nextInput = null; - nextInputSource = null; - - processEvent(e, nextType); - - if (newCurrentCanvasPosition != null && newLastCanvasPosition != null) { - currentCanvasPosition.setLocation(newCurrentCanvasPosition); - lastCanvasPosition.setLocation(newLastCanvasPosition); - } - } - - /** - * Flags the given event as needing to be processed. - * - * @param event the event to be processed - * @param type type of event to be processed - * @param camera camera from which the event was dispatched - */ - public void processEventFromCamera(final InputEvent event, final int type, final PCamera camera) { - // queue input - nextInput = event; - nextType = type; - nextInputSource = camera; - - // tell root to process queued inputs - camera.getRoot().processInputs(); - } - - /** - * Dispatches the given event to the listener, or does nothing if listener - * is null. - * - * @param event event to be dispatched - * @param type type of event to dispatch - * @param listener target of dispatch - */ - private void dispatchEventToListener(final PInputEvent event, final int type, final PInputEventListener listener) { - if (listener != null) { - // clear the handled bit since the same event object is used to send - // multiple events such as mouseEntered/mouseExited and mouseMove. - event.setHandled(false); - listener.processEvent(event, type); - } - } + /** Records the last known mouse position on the canvas. */ + private final Point2D lastCanvasPosition; + + /** Records the current known mouse position on the canvas. */ + private final Point2D currentCanvasPosition; + + /** The next InputEvent that needs to be processed. */ + private InputEvent nextInput; + + /** The type of the next InputEvent that needs to be processed. */ + private int nextType; + + /** The Input Source the next event to process came from. */ + private PCamera nextInputSource; + + /** The current mouse focus. */ + private PPickPath mouseFocus; + + /** The previous mouse focus. */ + private PPickPath previousMouseFocus; + + /** Tracks where the mouse is right now on the canvas. */ + private PPickPath mouseOver; + + /** Tracks the previous location of the mouse on the canvas. */ + private PPickPath previousMouseOver; + + /** Tracks the input event listener that should receive keyboard events. */ + private PInputEventListener keyboardFocus; + + /** Tracks the number mouse buttons currently pressed. */ + private int buttonsPressed; + + /** + * Creates a PInputManager and sets positions (last, current) to the origin + * (0,0). + */ + public PInputManager() { + lastCanvasPosition = new Point2D.Double(); + currentCanvasPosition = new Point2D.Double(); + } + + /** + * Return the node that currently has the keyboard focus. This node receives the + * key events. + * + * @return the current keyboard focus + */ + public PInputEventListener getKeyboardFocus() { + return keyboardFocus; + } + + /** + * Set the node that should receive key events. + * + * @param eventHandler sets the keyboard event focus, may be null + */ + public void setKeyboardFocus(final PInputEventListener eventHandler) { + final PInputEvent focusEvent = new PInputEvent(this, null); + + if (keyboardFocus != null) { + dispatchEventToListener(focusEvent, FocusEvent.FOCUS_LOST, keyboardFocus); + } + + keyboardFocus = eventHandler; + + if (keyboardFocus != null) { + dispatchEventToListener(focusEvent, FocusEvent.FOCUS_GAINED, keyboardFocus); + } + } + + /** + * Return the current Pick Path under the mouse focus. This will return the path + * that received the current mouse pressed event, or null if the mouse is not + * pressed. The mouse focus gets mouse dragged events even what the mouse is not + * over the mouse focus. + * + * @return the current Pick Path under the mouse focus + */ + public PPickPath getMouseFocus() { + return mouseFocus; + } + + /** + * Sets the current Pick Path under the mouse focus. The mouse focus gets mouse + * dragged events even when the mouse is not over the mouse focus. + * + * @param path the new mouse focus + */ + public void setMouseFocus(final PPickPath path) { + previousMouseFocus = mouseFocus; + mouseFocus = path; + } + + /** + * Return the node the the mouse is currently over. + * + * @return the path over which the mouse currently is + */ + public PPickPath getMouseOver() { + return mouseOver; + } + + /** + * Records the path which is directly below the mouse. + * + * @param path path over which the mouse has been moved + */ + public void setMouseOver(final PPickPath path) { + mouseOver = path; + } + + /** + * Returns the position on the Canvas of the last event. + * + * @return position of last canvas event + */ + public Point2D getLastCanvasPosition() { + return lastCanvasPosition; + } + + /** + * Returns the position of the current canvas event. + * + * @return position of current canvas event + */ + public Point2D getCurrentCanvasPosition() { + return currentCanvasPosition; + } + + // **************************************************************** + // Event Handling - Methods for handling events + // + // The dispatch manager updates the focus nodes based on the + // incoming events, and dispatches those events to the appropriate + // focus nodes. + // **************************************************************** + + /** {@inheritDoc} */ + public void keyPressed(final PInputEvent event) { + dispatchEventToListener(event, KeyEvent.KEY_PRESSED, keyboardFocus); + } + + /** {@inheritDoc} */ + public void keyReleased(final PInputEvent event) { + dispatchEventToListener(event, KeyEvent.KEY_RELEASED, keyboardFocus); + } + + /** {@inheritDoc} */ + public void keyTyped(final PInputEvent event) { + dispatchEventToListener(event, KeyEvent.KEY_TYPED, keyboardFocus); + } + + /** {@inheritDoc} */ + public void mouseClicked(final PInputEvent event) { + dispatchEventToListener(event, MouseEvent.MOUSE_CLICKED, previousMouseFocus); + } + + /** {@inheritDoc} */ + public void mouseWheelRotated(final PInputEvent event) { + setMouseFocus(getMouseOver()); + dispatchEventToListener(event, MouseWheelEvent.WHEEL_UNIT_SCROLL, mouseOver); + } + + /** {@inheritDoc} */ + public void mouseWheelRotatedByBlock(final PInputEvent event) { + setMouseFocus(getMouseOver()); + dispatchEventToListener(event, MouseWheelEvent.WHEEL_BLOCK_SCROLL, mouseOver); + } + + /** {@inheritDoc} */ + public void mouseDragged(final PInputEvent event) { + checkForMouseEnteredAndExited(event); + dispatchEventToListener(event, MouseEvent.MOUSE_DRAGGED, mouseFocus); + } + + /** {@inheritDoc} */ + public void mouseEntered(final PInputEvent event) { + dispatchEventToListener(event, MouseEvent.MOUSE_ENTERED, mouseOver); + } + + /** {@inheritDoc} */ + public void mouseExited(final PInputEvent event) { + dispatchEventToListener(event, MouseEvent.MOUSE_EXITED, previousMouseOver); + } + + /** {@inheritDoc} */ + public void mouseMoved(final PInputEvent event) { + checkForMouseEnteredAndExited(event); + dispatchEventToListener(event, MouseEvent.MOUSE_MOVED, mouseOver); + } + + /** {@inheritDoc} */ + public void mousePressed(final PInputEvent event) { + if (buttonsPressed == 0) { + setMouseFocus(getMouseOver()); + } + buttonsPressed++; + dispatchEventToListener(event, MouseEvent.MOUSE_PRESSED, mouseFocus); + if (buttonsPressed < 1 || buttonsPressed > 3) { + System.err.println("invalid pressedCount on mouse pressed: " + buttonsPressed); + } + } + + /** {@inheritDoc} */ + public void mouseReleased(final PInputEvent event) { + buttonsPressed--; + checkForMouseEnteredAndExited(event); + dispatchEventToListener(event, MouseEvent.MOUSE_RELEASED, mouseFocus); + if (buttonsPressed == 0) { + setMouseFocus(null); + } + if (buttonsPressed < 0 || buttonsPressed > 2) { + System.err.println("invalid pressedCount on mouse released: " + buttonsPressed); + } + } + + /** + * Fires events whenever the mouse moves from PNode to PNode. + * + * @param event to check to see if the top node has changed. + */ + protected void checkForMouseEnteredAndExited(final PInputEvent event) { + final PNode currentNode = getPickedNode(mouseOver); + final PNode previousNode = getPickedNode(previousMouseOver); + + if (currentNode != previousNode) { + dispatchEventToListener(event, MouseEvent.MOUSE_EXITED, previousMouseOver); + dispatchEventToListener(event, MouseEvent.MOUSE_ENTERED, mouseOver); + previousMouseOver = mouseOver; + } + } + + /** + * Returns picked node on pickPath if pickPath is not null, or null. + * + * @param pickPath from which to extract picked node + * + * @return the picked node or null if pickPath is null + */ + private PNode getPickedNode(final PPickPath pickPath) { + if (pickPath == null) { + return null; + } else { + return pickPath.getPickedNode(); + } + } + + // **************************************************************** + // Event Dispatch. + // **************************************************************** + /** {@inheritDoc} */ + public void processInput() { + if (nextInput == null) { + return; + } + + final PInputEvent e = new PInputEvent(this, nextInput); + + Point2D newCurrentCanvasPosition = null; + Point2D newLastCanvasPosition = null; + + if (e.isMouseEvent()) { + if (e.isMouseEnteredOrMouseExited()) { + final PPickPath aPickPath = nextInputSource.pick(((MouseEvent) nextInput).getX(), + ((MouseEvent) nextInput).getY(), 1); + setMouseOver(aPickPath); + previousMouseOver = aPickPath; + newCurrentCanvasPosition = (Point2D) currentCanvasPosition.clone(); + newLastCanvasPosition = (Point2D) lastCanvasPosition.clone(); + } else { + lastCanvasPosition.setLocation(currentCanvasPosition); + currentCanvasPosition.setLocation(((MouseEvent) nextInput).getX(), ((MouseEvent) nextInput).getY()); + final PPickPath aPickPath = nextInputSource.pick(currentCanvasPosition.getX(), + currentCanvasPosition.getY(), 1); + setMouseOver(aPickPath); + } + } + + nextInput = null; + nextInputSource = null; + + processEvent(e, nextType); + + if (newCurrentCanvasPosition != null && newLastCanvasPosition != null) { + currentCanvasPosition.setLocation(newCurrentCanvasPosition); + lastCanvasPosition.setLocation(newLastCanvasPosition); + } + } + + /** + * Flags the given event as needing to be processed. + * + * @param event the event to be processed + * @param type type of event to be processed + * @param camera camera from which the event was dispatched + */ + public void processEventFromCamera(final InputEvent event, final int type, final PCamera camera) { + // queue input + nextInput = event; + nextType = type; + nextInputSource = camera; + + // tell root to process queued inputs + camera.getRoot().processInputs(); + } + + /** + * Dispatches the given event to the listener, or does nothing if listener is + * null. + * + * @param event event to be dispatched + * @param type type of event to dispatch + * @param listener target of dispatch + */ + private void dispatchEventToListener(final PInputEvent event, final int type, final PInputEventListener listener) { + if (listener != null) { + // clear the handled bit since the same event object is used to send + // multiple events such as mouseEntered/mouseExited and mouseMove. + event.setHandled(false); + listener.processEvent(event, type); + } + } } diff --git a/core/src/main/java/org/piccolo2d/PLayer.java b/core/src/main/java/org/piccolo2d/PLayer.java index 07b9680e..94a807f2 100644 --- a/core/src/main/java/org/piccolo2d/PLayer.java +++ b/core/src/main/java/org/piccolo2d/PLayer.java @@ -37,7 +37,6 @@ import org.piccolo2d.util.PBounds; import org.piccolo2d.util.PObjectOutputStream; - /** * PLayer is a node that can be viewed directly by multiple camera nodes. * Generally child nodes are added to a layer to give the viewing cameras @@ -55,223 +54,219 @@ * @author Jesse Grosjean */ public class PLayer extends PNode { - /** - * Allows for future serialization code to understand versioned binary - * formats. - */ - private static final long serialVersionUID = 1L; + /** + * Allows for future serialization code to understand versioned binary formats. + */ + private static final long serialVersionUID = 1L; - /** - * The property name that identifies a change in the set of this layer's - * cameras (see {@link #getCamera getCamera}, {@link #getCameraCount - * getCameraCount}, {@link #getCamerasReference getCamerasReference}). In - * any property change event the new value will be a reference to the list - * of cameras, but old value will always be null. - */ - public static final String PROPERTY_CAMERAS = "cameras"; + /** + * The property name that identifies a change in the set of this layer's cameras + * (see {@link #getCamera getCamera}, {@link #getCameraCount getCameraCount}, + * {@link #getCamerasReference getCamerasReference}). In any property change + * event the new value will be a reference to the list of cameras, but old value + * will always be null. + */ + public static final String PROPERTY_CAMERAS = "cameras"; - /** - * The property code that identifies a change in the set of this layer's - * cameras (see {@link #getCamera getCamera}, {@link #getCameraCount - * getCameraCount}, {@link #getCamerasReference getCamerasReference}). In - * any property change event the new value will be a reference to the list - * of cameras, but old value will always be null. - */ - public static final int PROPERTY_CODE_CAMERAS = 1 << 13; + /** + * The property code that identifies a change in the set of this layer's cameras + * (see {@link #getCamera getCamera}, {@link #getCameraCount getCameraCount}, + * {@link #getCamerasReference getCamerasReference}). In any property change + * event the new value will be a reference to the list of cameras, but old value + * will always be null. + */ + public static final int PROPERTY_CODE_CAMERAS = 1 << 13; - /** - * Cameras which are registered as viewers of this PLayer. - */ - private transient List cameras; + /** + * Cameras which are registered as viewers of this PLayer. + */ + private transient List cameras; - /** - * Creates a PLayer without any cameras attached to it. - */ - public PLayer() { - super(); - cameras = new ArrayList(); - } + /** + * Creates a PLayer without any cameras attached to it. + */ + public PLayer() { + super(); + cameras = new ArrayList(); + } - // **************************************************************** - // Cameras - Maintain the list of cameras that are viewing this - // layer. - // **************************************************************** + // **************************************************************** + // Cameras - Maintain the list of cameras that are viewing this + // layer. + // **************************************************************** - /** - * Get the list of cameras viewing this layer. - * - * @return direct reference to registered cameras - */ - public List getCamerasReference() { - return cameras; - } + /** + * Get the list of cameras viewing this layer. + * + * @return direct reference to registered cameras + */ + public List getCamerasReference() { + return cameras; + } - /** - * Get the number of cameras viewing this layer. - * - * @return the number of cameras attached to this layer - */ - public int getCameraCount() { - if (cameras == null) { - return 0; - } - return cameras.size(); - } + /** + * Get the number of cameras viewing this layer. + * + * @return the number of cameras attached to this layer + */ + public int getCameraCount() { + if (cameras == null) { + return 0; + } + return cameras.size(); + } - /** - * Get the camera in this layer's camera list at the specified index. - * - * @param index index of camera to fetch - * @return camera at the given index - */ - public PCamera getCamera(final int index) { - return (PCamera) cameras.get(index); - } + /** + * Get the camera in this layer's camera list at the specified index. + * + * @param index index of camera to fetch + * @return camera at the given index + */ + public PCamera getCamera(final int index) { + return cameras.get(index); + } - /** - * Add a camera to this layer's camera list. This method it called - * automatically when a layer is added to a camera. - * - * @param camera the camera to add to this layer - */ - public void addCamera(final PCamera camera) { - addCamera(cameras.size(), camera); - } + /** + * Add a camera to this layer's camera list. This method it called automatically + * when a layer is added to a camera. + * + * @param camera the camera to add to this layer + */ + public void addCamera(final PCamera camera) { + addCamera(cameras.size(), camera); + } - /** - * Add a camera to this layer's camera list at the specified index. This - * method it called automatically when a layer is added to a camera. - * - * @param index index at which the camera should be inserted - * @param camera Camera to add to layer - */ - public void addCamera(final int index, final PCamera camera) { - cameras.add(index, camera); - invalidatePaint(); - firePropertyChange(PROPERTY_CODE_CAMERAS, PROPERTY_CAMERAS, null, cameras); - } + /** + * Add a camera to this layer's camera list at the specified index. This method + * it called automatically when a layer is added to a camera. + * + * @param index index at which the camera should be inserted + * @param camera Camera to add to layer + */ + public void addCamera(final int index, final PCamera camera) { + cameras.add(index, camera); + invalidatePaint(); + firePropertyChange(PROPERTY_CODE_CAMERAS, PROPERTY_CAMERAS, null, cameras); + } - /** - * Remove the camera from this layer's camera list. - * - * @param camera the camera to remove from the layer, does nothing if not - * found - * @return camera that was passed in - */ - public PCamera removeCamera(final PCamera camera) { - if (cameras.remove(camera)) { - invalidatePaint(); - firePropertyChange(PROPERTY_CODE_CAMERAS, PROPERTY_CAMERAS, null, cameras); - } - return camera; - } + /** + * Remove the camera from this layer's camera list. + * + * @param camera the camera to remove from the layer, does nothing if not found + * @return camera that was passed in + */ + public PCamera removeCamera(final PCamera camera) { + if (cameras.remove(camera)) { + invalidatePaint(); + firePropertyChange(PROPERTY_CODE_CAMERAS, PROPERTY_CAMERAS, null, cameras); + } + return camera; + } - /** - * Remove the camera at the given index from this layer's camera list. - * - * @param index the index of the camera we wish to remove - * - * @return camera that was removed - */ - public PCamera removeCamera(final int index) { - final PCamera result = (PCamera) cameras.remove(index); - invalidatePaint(); - firePropertyChange(PROPERTY_CODE_CAMERAS, PROPERTY_CAMERAS, null, cameras); - return result; - } + /** + * Remove the camera at the given index from this layer's camera list. + * + * @param index the index of the camera we wish to remove + * + * @return camera that was removed + */ + public PCamera removeCamera(final int index) { + final PCamera result = cameras.remove(index); + invalidatePaint(); + firePropertyChange(PROPERTY_CODE_CAMERAS, PROPERTY_CAMERAS, null, cameras); + return result; + } - // **************************************************************** - // Camera Repaint Notifications - Layer nodes must forward their - // repaints to each camera that is viewing them so that the camera - // views will also get repainted. - // **************************************************************** + // **************************************************************** + // Camera Repaint Notifications - Layer nodes must forward their + // repaints to each camera that is viewing them so that the camera + // views will also get repainted. + // **************************************************************** - /** - * Override repaints and forward them to the cameras that are viewing this - * layer. - * - * @param localBounds bounds flagged as needing repainting - * @param repaintSource the source of the repaint notification - */ - public void repaintFrom(final PBounds localBounds, final PNode repaintSource) { - if (repaintSource != this) { - localToParent(localBounds); - } + /** + * Override repaints and forward them to the cameras that are viewing this + * layer. + * + * @param localBounds bounds flagged as needing repainting + * @param repaintSource the source of the repaint notification + */ + public void repaintFrom(final PBounds localBounds, final PNode repaintSource) { + if (repaintSource != this) { + localToParent(localBounds); + } - notifyCameras(localBounds); + notifyCameras(localBounds); - if (getParent() != null) { - getParent().repaintFrom(localBounds, repaintSource); - } - } + if (getParent() != null) { + getParent().repaintFrom(localBounds, repaintSource); + } + } - /** - * Dispatches repaint notification to all registered cameras. - * - * @param parentBounds bounds needing repainting in parent coordinate system - */ - protected void notifyCameras(final PBounds parentBounds) { - final int count = getCameraCount(); - for (int i = 0; i < count; i++) { - final PCamera each = (PCamera) cameras.get(i); - each.repaintFromLayer(parentBounds, this); - } - } + /** + * Dispatches repaint notification to all registered cameras. + * + * @param parentBounds bounds needing repainting in parent coordinate system + */ + protected void notifyCameras(final PBounds parentBounds) { + final int count = getCameraCount(); + for (int i = 0; i < count; i++) { + final PCamera each = cameras.get(i); + each.repaintFromLayer(parentBounds, this); + } + } - // **************************************************************** - // Serialization - Layers conditionally serialize their cameras. - // This means that only the camera references that were unconditionally - // (using writeObject) serialized by someone else will be restored - // when the layer is unserialized. - // **************************************************************** + // **************************************************************** + // Serialization - Layers conditionally serialize their cameras. + // This means that only the camera references that were unconditionally + // (using writeObject) serialized by someone else will be restored + // when the layer is unserialized. + // **************************************************************** - /** - * Write this layer and all its children out to the given stream. Note that - * the layer writes out any cameras that are viewing it conditionally, so - * they will only get written out if someone else writes them - * unconditionally. - * - * @param out object to which the layer should be streamed - * @throws IOException may occur while serializing to stream - */ - private void writeObject(final ObjectOutputStream out) throws IOException { - if (!(out instanceof PObjectOutputStream)) { - throw new RuntimeException("May not serialize PLayer to a non PObjectOutputStream"); - } - out.defaultWriteObject(); + /** + * Write this layer and all its children out to the given stream. Note that the + * layer writes out any cameras that are viewing it conditionally, so they will + * only get written out if someone else writes them unconditionally. + * + * @param out object to which the layer should be streamed + * @throws IOException may occur while serializing to stream + */ + private void writeObject(final ObjectOutputStream out) throws IOException { + if (!(out instanceof PObjectOutputStream)) { + throw new RuntimeException("May not serialize PLayer to a non PObjectOutputStream"); + } + out.defaultWriteObject(); - final int count = getCameraCount(); - for (int i = 0; i < count; i++) { - ((PObjectOutputStream) out).writeConditionalObject(cameras.get(i)); - } + final int count = getCameraCount(); + for (int i = 0; i < count; i++) { + ((PObjectOutputStream) out).writeConditionalObject(cameras.get(i)); + } - out.writeObject(Boolean.FALSE); - } + out.writeObject(Boolean.FALSE); + } - /** - * Deserializes PLayer from the provided ObjectInputStream. - * - * @param in stream from which PLayer should be read - * - * @throws IOException since it involves quite a bit of IO - * @throws ClassNotFoundException may occur is serialized stream has been - * renamed after serialization - */ - private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { - in.defaultReadObject(); + /** + * Deserializes PLayer from the provided ObjectInputStream. + * + * @param in stream from which PLayer should be read + * + * @throws IOException since it involves quite a bit of IO + * @throws ClassNotFoundException may occur is serialized stream has been + * renamed after serialization + */ + private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { + in.defaultReadObject(); - cameras = new ArrayList(); + cameras = new ArrayList(); - while (true) { - final Object each = in.readObject(); - if (each != null) { - if (each.equals(Boolean.FALSE)) { - break; - } - else { - cameras.add(each); - } - } - } - } + while (true) { + final Object each = in.readObject(); + if (each != null) { + if (each.equals(Boolean.FALSE)) { + break; + } else { + cameras.add((PCamera) each); + } + } + } + } } diff --git a/core/src/main/java/org/piccolo2d/PNode.java b/core/src/main/java/org/piccolo2d/PNode.java index 873c0e86..e79e7af3 100644 --- a/core/src/main/java/org/piccolo2d/PNode.java +++ b/core/src/main/java/org/piccolo2d/PNode.java @@ -82,7 +82,6 @@ import org.piccolo2d.util.PPickPath; import org.piccolo2d.util.PUtil; - /** * PNode is the central abstraction in Piccolo. All objects that are * visible on the screen are instances of the node class. All nodes may have @@ -96,3703 +95,3649 @@ * @author Jesse Grosjean */ public class PNode implements Cloneable, Serializable, Printable { - /** - * The minimum difference in transparency required before the transparency - * is allowed to change. Done for efficiency reasons. I doubt very much that - * the human eye could tell the difference between 0.01 and 0.02 - * transparency. - */ - private static final float TRANSPARENCY_RESOLUTION = 0.01f; - - /** - * Allows for future serialization code to understand versioned binary - * formats. - */ - private static final long serialVersionUID = 1L; - - /** - * The property name that identifies a change in this node's client - * properties (see {@link #getClientProperties getClientProperties}). In an - * property change event the new value will be a reference to the map of - * client properties but old value will always be null. - */ - public static final String PROPERTY_CLIENT_PROPERTIES = "clientProperties"; - - /** - * The property code that identifies a change in this node's client - * properties (see {@link #getClientProperties getClientProperties}). In an - * property change event the new value will be a reference to the map of - * client properties but old value will always be null. - */ - public static final int PROPERTY_CODE_CLIENT_PROPERTIES = 1 << 0; - - /** - * The property name that identifies a change of this node's bounds (see - * {@link #getBounds getBounds}, {@link #getBoundsReference - * getBoundsReference}). In any property change event the new value will be - * a reference to this node's bounds, but old value will always be null. - */ - public static final String PROPERTY_BOUNDS = "bounds"; - - /** - * The property code that identifies a change of this node's bounds (see - * {@link #getBounds getBounds}, {@link #getBoundsReference - * getBoundsReference}). In any property change event the new value will be - * a reference to this node's bounds, but old value will always be null. - */ - public static final int PROPERTY_CODE_BOUNDS = 1 << 1; - - /** - * The property name that identifies a change of this node's full bounds - * (see {@link #getFullBounds getFullBounds}, - * {@link #getFullBoundsReference getFullBoundsReference}). In any property - * change event the new value will be a reference to this node's full bounds - * cache, but old value will always be null. - */ - public static final String PROPERTY_FULL_BOUNDS = "fullBounds"; - - /** - * The property code that identifies a change of this node's full bounds - * (see {@link #getFullBounds getFullBounds}, - * {@link #getFullBoundsReference getFullBoundsReference}). In any property - * change event the new value will be a reference to this node's full bounds - * cache, but old value will always be null. - */ - public static final int PROPERTY_CODE_FULL_BOUNDS = 1 << 2; - - /** - * The property name that identifies a change of this node's transform (see - * {@link #getTransform getTransform}, {@link #getTransformReference - * getTransformReference}). In any property change event the new value will - * be a reference to this node's transform, but old value will always be - * null. - */ - public static final String PROPERTY_TRANSFORM = "transform"; - - /** - * The property code that identifies a change of this node's transform (see - * {@link #getTransform getTransform}, {@link #getTransformReference - * getTransformReference}). In any property change event the new value will - * be a reference to this node's transform, but old value will always be - * null. - */ - public static final int PROPERTY_CODE_TRANSFORM = 1 << 3; - - /** - * The property name that identifies a change of this node's visibility (see - * {@link #getVisible getVisible}). Both old value and new value will be - * null in any property change event. - */ - public static final String PROPERTY_VISIBLE = "visible"; - - /** - * The property code that identifies a change of this node's visibility (see - * {@link #getVisible getVisible}). Both old value and new value will be - * null in any property change event. - */ - public static final int PROPERTY_CODE_VISIBLE = 1 << 4; - - /** - * The property name that identifies a change of this node's paint (see - * {@link #getPaint getPaint}). Both old value and new value will be set - * correctly in any property change event. - */ - public static final String PROPERTY_PAINT = "paint"; - - /** - * The property code that identifies a change of this node's paint (see - * {@link #getPaint getPaint}). Both old value and new value will be set - * correctly in any property change event. - */ - public static final int PROPERTY_CODE_PAINT = 1 << 5; - - /** - * The property name that identifies a change of this node's transparency - * (see {@link #getTransparency getTransparency}). Both old value and new - * value will be null in any property change event. - */ - public static final String PROPERTY_TRANSPARENCY = "transparency"; - - /** - * The property code that identifies a change of this node's transparency - * (see {@link #getTransparency getTransparency}). Both old value and new - * value will be null in any property change event. - */ - public static final int PROPERTY_CODE_TRANSPARENCY = 1 << 6; - - /** - * The property name that identifies a change of this node's pickable status - * (see {@link #getPickable getPickable}). Both old value and new value will - * be null in any property change event. - */ - public static final String PROPERTY_PICKABLE = "pickable"; - /** - * The property code that identifies a change of this node's pickable status - * (see {@link #getPickable getPickable}). Both old value and new value will - * be null in any property change event. - */ - public static final int PROPERTY_CODE_PICKABLE = 1 << 7; - - /** - * The property name that identifies a change of this node's children - * pickable status (see {@link #getChildrenPickable getChildrenPickable}). - * Both old value and new value will be null in any property change event. - */ - public static final String PROPERTY_CHILDREN_PICKABLE = "childrenPickable"; - - /** - * The property code that identifies a change of this node's children - * pickable status (see {@link #getChildrenPickable getChildrenPickable}). - * Both old value and new value will be null in any property change event. - */ - public static final int PROPERTY_CODE_CHILDREN_PICKABLE = 1 << 8; - - /** - * The property name that identifies a change in the set of this node's - * direct children (see {@link #getChildrenReference getChildrenReference}, - * {@link #getChildrenIterator getChildrenIterator}). In any property change - * event the new value will be a reference to this node's children, but old - * value will always be null. - */ - public static final String PROPERTY_CHILDREN = "children"; - - /** - * The property code that identifies a change in the set of this node's - * direct children (see {@link #getChildrenReference getChildrenReference}, - * {@link #getChildrenIterator getChildrenIterator}). In any property change - * event the new value will be a reference to this node's children, but old - * value will always be null. - */ - public static final int PROPERTY_CODE_CHILDREN = 1 << 9; - - /** - * The property name that identifies a change of this node's parent (see - * {@link #getParent getParent}). Both old value and new value will be set - * correctly in any property change event. - */ - public static final String PROPERTY_PARENT = "parent"; - - /** - * The property code that identifies a change of this node's parent (see - * {@link #getParent getParent}). Both old value and new value will be set - * correctly in any property change event. - */ - public static final int PROPERTY_CODE_PARENT = 1 << 10; - - /** Is an optimization for use during repaints. */ - private static final PBounds TEMP_REPAINT_BOUNDS = new PBounds(); - - /** The single scene graph delegate that receives low level node events. */ - public static PSceneGraphDelegate SCENE_GRAPH_DELEGATE = null; - - /** Tracks the parent of this node, may be null. */ - private transient PNode parent; - - /** Tracks all immediate child nodes. */ - private List children; - - /** Bounds of the PNode. */ - private final PBounds bounds; - - /** Transform that applies to this node in relation to its parent. */ - private PAffineTransform transform; - - /** The paint to use for the background of this node. */ - private Paint paint; - - /** - * How Opaque this node should be 1f = fully opaque, 0f = completely - * transparent. - */ - private float transparency; - - /** A modifiable set of client properties. */ - private MutableAttributeSet clientProperties; - - /** - * An optimization that remembers the full bounds of a node rather than - * computing it every time. - */ - private PBounds fullBoundsCache; - - /** - * Mask used when deciding whether to bubble up property change events to - * parents. - */ - private int propertyChangeParentMask = 0; - - /** Used to handle property change listeners. */ - private transient SwingPropertyChangeSupport changeSupport; - - /** List of event listeners. */ - private transient EventListenerList listenerList; - - /** Whether this node is pickable or not. */ - private boolean pickable; - - /** - * Whether to stop processing pick at this node and not bother drilling down - * into children. - */ - private boolean childrenPickable; - - /** Whether this node will be rendered. */ - private boolean visible; - - private boolean childBoundsVolatile; - - /** Whether this node needs to be repainted. */ - private boolean paintInvalid; - - /** Whether children need to be repainted. */ - private boolean childPaintInvalid; - - /** Whether this node's bounds have changed, and so needs to be relaid out. */ - private boolean boundsChanged; - - /** Whether this node's full bounds need to be recomputed. */ - private boolean fullBoundsInvalid; - - /** Whether this node's child bounds need to be recomputed. */ - private boolean childBoundsInvalid; - - private boolean occluded; - - /** Stores the name associated to this node. */ - private String name; - - /** - * toImage fill strategy that stretches the node be as large as possible - * while still retaining its aspect ratio. - * - * @since 1.3 - */ - public static final int FILL_STRATEGY_ASPECT_FIT = 1; - - /** - * toImage fill strategy that stretches the node be large enough to cover - * the image, and centers it. - * - * @since 1.3 - */ - public static final int FILL_STRATEGY_ASPECT_COVER = 2; - - /** - * toImage fill strategy that stretches the node to be exactly the - * dimensions of the image. Will result in distortion if the aspect ratios - * are different. - * - * @since 1.3 - */ - public static final int FILL_STRATEGY_EXACT_FIT = 4; - - /** - * Creates a new PNode with the given name. - * - * @since 1.3 - * @param newName name to assign to node - */ - public PNode(final String newName) { - this(); - setName(newName); - } - - /** - * Constructs a new PNode. - *

- * By default a node's paint is null, and bounds are empty. These values - * must be set for the node to show up on the screen once it's added to a - * scene graph. - */ - public PNode() { - bounds = new PBounds(); - fullBoundsCache = new PBounds(); - transparency = 1.0f; - pickable = true; - childrenPickable = true; - visible = true; - } - - // **************************************************************** - // Animation - Methods to animate this node. - // - // Note that animation is implemented by activities (PActivity), - // so if you need more control over your animation look at the - // activities package. Each animate method creates an animation that - // will animate the node from its current state to the new state - // specified over the given duration. These methods will try to - // automatically schedule the new activity, but if the node does not - // descend from the root node when the method is called then the - // activity will not be scheduled and you must schedule it manually. - // **************************************************************** - - /** - * Animate this node's bounds from their current location when the activity - * starts to the specified bounds. If this node descends from the root then - * the activity will be scheduled, else the returned activity should be - * scheduled manually. If two different transform activities are scheduled - * for the same node at the same time, they will both be applied to the - * node, but the last one scheduled will be applied last on each frame, so - * it will appear to have replaced the original. Generally you will not want - * to do that. Note this method animates the node's bounds, but does not - * change the node's transform. Use animateTransformToBounds() to animate - * the node's transform instead. - * - * @param x left of target bounds - * @param y top of target bounds - * @param width width of target bounds - * @param height height of target bounds - * @param duration amount of time that the animation should take - * @return the newly scheduled activity - */ - public PInterpolatingActivity animateToBounds(final double x, final double y, final double width, - final double height, final long duration) { - if (duration == 0) { - setBounds(x, y, width, height); - return null; - } - - final PBounds dst = new PBounds(x, y, width, height); - - final PInterpolatingActivity interpolatingActivity = new PInterpolatingActivity(duration, - PUtil.DEFAULT_ACTIVITY_STEP_RATE) { - private PBounds src; - - protected void activityStarted() { - src = getBounds(); - startResizeBounds(); - super.activityStarted(); - } - - public void setRelativeTargetValue(final float zeroToOne) { - PNode.this.setBounds(src.x + zeroToOne * (dst.x - src.x), src.y + zeroToOne * (dst.y - src.y), - src.width + zeroToOne * (dst.width - src.width), src.height + zeroToOne - * (dst.height - src.height)); - } - - protected void activityFinished() { - super.activityFinished(); - endResizeBounds(); - } - }; - - addActivity(interpolatingActivity); - return interpolatingActivity; - } - - /** - * Animate this node from it's current transform when the activity starts a - * new transform that will fit the node into the given bounds. If this node - * descends from the root then the activity will be scheduled, else the - * returned activity should be scheduled manually. If two different - * transform activities are scheduled for the same node at the same time, - * they will both be applied to the node, but the last one scheduled will be - * applied last on each frame, so it will appear to have replaced the - * original. Generally you will not want to do that. Note this method - * animates the node's transform, but does not directly change the node's - * bounds rectangle. Use animateToBounds() to animate the node's bounds - * rectangle instead. - * - * @param x left of target bounds - * @param y top of target bounds - * @param width width of target bounds - * @param height height of target bounds - * @param duration amount of time that the animation should take - * @return the newly scheduled activity - */ - public PTransformActivity animateTransformToBounds(final double x, final double y, final double width, - final double height, final long duration) { - final PAffineTransform t = new PAffineTransform(); - t.setToScale(width / getWidth(), height / getHeight()); - final double scale = t.getScale(); - t.setOffset(x - getX() * scale, y - getY() * scale); - return animateToTransform(t, duration); - } - - /** - * Animate this node's transform from its current location when the activity - * starts to the specified location, scale, and rotation. If this node - * descends from the root then the activity will be scheduled, else the - * returned activity should be scheduled manually. If two different - * transform activities are scheduled for the same node at the same time, - * they will both be applied to the node, but the last one scheduled will be - * applied last on each frame, so it will appear to have replaced the - * original. Generally you will not want to do that. - * - * @param x the final target x position of node - * @param y the final target y position of node - * @param duration amount of time that the animation should take - * @param scale the final scale for the duration - * @param theta final theta value (in radians) for the animation - * @return the newly scheduled activity - */ - public PTransformActivity animateToPositionScaleRotation(final double x, final double y, final double scale, - final double theta, final long duration) { - final PAffineTransform t = getTransform(); - t.setOffset(x, y); - t.setScale(scale); - t.setRotation(theta); - return animateToTransform(t, duration); - } - - /** - * Animate this node's transform from its current values when the activity - * starts to the new values specified in the given transform. If this node - * descends from the root then the activity will be scheduled, else the - * returned activity should be scheduled manually. If two different - * transform activities are scheduled for the same node at the same time, - * they will both be applied to the node, but the last one scheduled will be - * applied last on each frame, so it will appear to have replaced the - * original. Generally you will not want to do that. - * - * @param destTransform the final transform value - * @param duration amount of time that the animation should take - * @return the newly scheduled activity - */ - public PTransformActivity animateToTransform(final AffineTransform destTransform, final long duration) { - if (duration == 0) { - setTransform(destTransform); - return null; - } - else { - final PTransformActivity.Target t = new PTransformActivity.Target() { - public void setTransform(final AffineTransform aTransform) { - PNode.this.setTransform(aTransform); - } - - public void getSourceMatrix(final double[] aSource) { - PNode.this.getTransformReference(true).getMatrix(aSource); - } - }; - - final PTransformActivity ta = new PTransformActivity(duration, PUtil.DEFAULT_ACTIVITY_STEP_RATE, t, - destTransform); - addActivity(ta); - return ta; - } - } - - /** - * Animate this node's color from its current value to the new value - * specified. This meathod assumes that this nodes paint property is of type - * color. If this node descends from the root then the activity will be - * scheduled, else the returned activity should be scheduled manually. If - * two different color activities are scheduled for the same node at the - * same time, they will both be applied to the node, but the last one - * scheduled will be applied last on each frame, so it will appear to have - * replaced the original. Generally you will not want to do that. - * - * @param destColor final color value. - * @param duration amount of time that the animation should take - * @return the newly scheduled activity - */ - public PInterpolatingActivity animateToColor(final Color destColor, final long duration) { - if (duration == 0) { - setPaint(destColor); - return null; - } - else { - final PColorActivity.Target t = new PColorActivity.Target() { - public Color getColor() { - return (Color) getPaint(); - } - - public void setColor(final Color color) { - setPaint(color); - } - }; - - final PColorActivity ca = new PColorActivity(duration, PUtil.DEFAULT_ACTIVITY_STEP_RATE, t, destColor); - addActivity(ca); - return ca; - } - } - - /** - * Animate this node's transparency from its current value to the new value - * specified. Transparency values must range from zero to one. If this node - * descends from the root then the activity will be scheduled, else the - * returned activity should be scheduled manually. If two different - * transparency activities are scheduled for the same node at the same time, - * they will both be applied to the node, but the last one scheduled will be - * applied last on each frame, so it will appear to have replaced the - * original. Generally you will not want to do that. - * - * @param zeroToOne final transparency value. - * @param duration amount of time that the animation should take - * @return the newly scheduled activity - */ - public PInterpolatingActivity animateToTransparency(final float zeroToOne, final long duration) { - if (duration == 0) { - setTransparency(zeroToOne); - return null; - } - else { - final float dest = zeroToOne; - - final PInterpolatingActivity ta = new PInterpolatingActivity(duration, PUtil.DEFAULT_ACTIVITY_STEP_RATE) { - private float source; - - protected void activityStarted() { - source = getTransparency(); - super.activityStarted(); - } - - public void setRelativeTargetValue(final float zeroToOne) { - PNode.this.setTransparency(source + zeroToOne * (dest - source)); - } - }; - - addActivity(ta); - return ta; - } - } - - /** - * Schedule the given activity with the root, note that only scheduled - * activities will be stepped. If the activity is successfully added true is - * returned, else false. - * - * @param activity new activity to schedule - * @return true if the activity is successfully scheduled. - */ - public boolean addActivity(final PActivity activity) { - final PRoot r = getRoot(); - if (r != null) { - return r.addActivity(activity); - } - return false; - } - - // **************************************************************** - // Client Properties - Methods for managing client properties for - // this node. - // - // Client properties provide a way for programmers to attach - // extra information to a node without having to subclass it and - // add new instance variables. - // **************************************************************** - - /** - * Return mutable attributed set of client properties associated with this - * node. - * - * @return the client properties associated to this node - */ - public MutableAttributeSet getClientProperties() { - if (clientProperties == null) { - clientProperties = new SimpleAttributeSet(); - } - return clientProperties; - } - - /** - * Returns the value of the client attribute with the specified key. Only - * attributes added with addAttribute will return a non-null - * value. - * - * @param key key to use while fetching client attribute - * - * @return the value of this attribute or null - */ - public Object getAttribute(final Object key) { - if (clientProperties == null || key == null) { - return null; - } - else { - return clientProperties.getAttribute(key); - } - } - - /** - * Add an arbitrary key/value to this node. - *

- * The get/add attribute methods provide access to a small - * per-instance attribute set. Callers can use get/add attribute to annotate - * nodes that were created by another module. - *

- * If value is null this method will remove the attribute. - * - * @param key to use when adding the attribute - * @param value value to associate to the new attribute - */ - public void addAttribute(final Object key, final Object value) { - if (value == null && clientProperties == null) { - return; - } - - final Object oldValue = getAttribute(key); - - if (value != oldValue) { - if (clientProperties == null) { - clientProperties = new SimpleAttributeSet(); - } - - if (value == null) { - clientProperties.removeAttribute(key); - } - else { - clientProperties.addAttribute(key, value); - } - - if (clientProperties.getAttributeCount() == 0 && clientProperties.getResolveParent() == null) { - clientProperties = null; - } - - firePropertyChange(PROPERTY_CODE_CLIENT_PROPERTIES, PROPERTY_CLIENT_PROPERTIES, null, clientProperties); - firePropertyChange(PROPERTY_CODE_CLIENT_PROPERTIES, key.toString(), oldValue, value); - } - } - - /** - * Returns an enumeration of all keys maped to attribute values values. - * - * @return an Enumeration over attribute keys - */ - public Enumeration getClientPropertyKeysEnumeration() { - if (clientProperties == null) { - return PUtil.NULL_ENUMERATION; - } - else { - return clientProperties.getAttributeNames(); - } - } - - // convenience methods for attributes - - /** - * Fetches the value of the requested attribute, returning defaultValue is - * not found. - * - * @param key attribute to search for - * @param defaultValue value to return if attribute is not found - * - * @return value of attribute or defaultValue if not found - */ - public Object getAttribute(final Object key, final Object defaultValue) { - final Object value = getAttribute(key); - if (value == null) { - return defaultValue; - } - - return value; - } - - /** - * Fetches the boolean value of the requested attribute, returning - * defaultValue is not found. - * - * @param key attribute to search for - * @param defaultValue value to return if attribute is not found - * - * @return value of attribute or defaultValue if not found - */ - public boolean getBooleanAttribute(final Object key, final boolean defaultValue) { - final Boolean value = (Boolean) getAttribute(key); - if (value == null) { - return defaultValue; - } - - return value.booleanValue(); - } - - /** - * Fetches the integer value of the requested attribute, returning - * defaultValue is not found. - * - * @param key attribute to search for - * @param defaultValue value to return if attribute is not found - * - * @return value of attribute or defaultValue if not found - */ - public int getIntegerAttribute(final Object key, final int defaultValue) { - final Number value = (Number) getAttribute(key); - if (value == null) { - return defaultValue; - } - - return value.intValue(); - } - - /** - * Fetches the double value of the requested attribute, returning - * defaultValue is not found. - * - * @param key attribute to search for - * @param defaultValue value to return if attribute is not found - * - * @return value of attribute or defaultValue if not found - */ - public double getDoubleAttribute(final Object key, final double defaultValue) { - final Number value = (Number) getAttribute(key); - if (value == null) { - return defaultValue; - } - - return value.doubleValue(); - } - - // **************************************************************** - // Copying - Methods for copying this node and its descendants. - // Copying is implemented in terms of serialization. - // **************************************************************** - - /** - * The copy method copies this node and all of its descendants. Note that - * copying is implemented in terms of java serialization. See the - * serialization notes for more information. - * - * @return new copy of this node or null if the node was not serializable - */ - public Object clone() { - try { - final byte[] ser = PObjectOutputStream.toByteArray(this); - return new ObjectInputStream(new ByteArrayInputStream(ser)).readObject(); - } - catch (final IOException e) { - return null; - } - catch (final ClassNotFoundException e) { - return null; - } - } - - // **************************************************************** - // Coordinate System Conversions - Methods for converting - // geometry between this nodes local coordinates and the other - // major coordinate systems. - // - // Each nodes has an affine transform that it uses to define its - // own coordinate system. For example if you create a new node and - // add it to the canvas it will appear in the upper right corner. Its - // coordinate system matches the coordinate system of its parent - // (the root node) at this point. But if you move this node by calling - // node.translate() the nodes affine transform will be modified and the - // node will appear at a different location on the screen. The node - // coordinate system no longer matches the coordinate system of its - // parent. - // - // This is useful because it means that the node's methods for - // rendering and picking don't need to worry about the fact that - // the node has been moved to another position on the screen, they - // keep working just like they did when it was in the upper right - // hand corner of the screen. - // - // The problem is now that each node defines its own coordinate - // system it is difficult to compare the positions of two node with - // each other. These methods are all meant to help solve that problem. - // - // The terms used in the methods are as follows: - // - // local - The local or base coordinate system of a node. - // parent - The coordinate system of a node's parent - // global - The topmost coordinate system, above the root node. - // - // Normally when comparing the positions of two nodes you will - // convert the local position of each node to the global coordinate - // system, and then compare the positions in that common coordinate - // system. - // *************************************************************** - - /** - * Transform the given point from this node's local coordinate system to its - * parent's local coordinate system. Note that this will modify the point - * parameter. - * - * @param localPoint point in local coordinate system to be transformed. - * @return point in parent's local coordinate system - */ - public Point2D localToParent(final Point2D localPoint) { - if (transform == null) { - return localPoint; - } - return transform.transform(localPoint, localPoint); - } - - /** - * Transform the given dimension from this node's local coordinate system to - * its parent's local coordinate system. Note that this will modify the - * dimension parameter. - * - * @param localDimension dimension in local coordinate system to be - * transformed. - * @return dimension in parent's local coordinate system - */ - public Dimension2D localToParent(final Dimension2D localDimension) { - if (transform == null) { - return localDimension; - } - return transform.transform(localDimension, localDimension); - } - - /** - * Transform the given rectangle from this node's local coordinate system to - * its parent's local coordinate system. Note that this will modify the - * rectangle parameter. - * - * @param localRectangle rectangle in local coordinate system to be - * transformed. - * @return rectangle in parent's local coordinate system - */ - public Rectangle2D localToParent(final Rectangle2D localRectangle) { - if (transform == null) { - return localRectangle; - } - return transform.transform(localRectangle, localRectangle); - } - - /** - * Transform the given point from this node's parent's local coordinate - * system to the local coordinate system of this node. Note that this will - * modify the point parameter. - * - * @param parentPoint point in parent's coordinate system to be transformed. - * @return point in this node's local coordinate system - */ - public Point2D parentToLocal(final Point2D parentPoint) { - if (transform == null) { - return parentPoint; - } - - return transform.inverseTransform(parentPoint, parentPoint); - } - - /** - * Transform the given dimension from this node's parent's local coordinate - * system to the local coordinate system of this node. Note that this will - * modify the dimension parameter. - * - * @param parentDimension dimension in parent's coordinate system to be - * transformed. - * @return dimension in this node's local coordinate system - */ - public Dimension2D parentToLocal(final Dimension2D parentDimension) { - if (transform == null) { - return parentDimension; - } - return transform.inverseTransform(parentDimension, parentDimension); - } - - /** - * Transform the given rectangle from this node's parent's local coordinate - * system to the local coordinate system of this node. Note that this will - * modify the rectangle parameter. - * - * @param parentRectangle rectangle in parent's coordinate system to be - * transformed. - * @return rectangle in this node's local coordinate system - */ - public Rectangle2D parentToLocal(final Rectangle2D parentRectangle) { - if (transform == null) { - return parentRectangle; - } - return transform.inverseTransform(parentRectangle, parentRectangle); - } - - /** - * Transform the given point from this node's local coordinate system to the - * global coordinate system. Note that this will modify the point parameter. - * - * @param localPoint point in local coordinate system to be transformed. - * @return point in global coordinates - */ - public Point2D localToGlobal(final Point2D localPoint) { - PNode n = this; - while (n != null) { - n.localToParent(localPoint); - n = n.parent; - } - return localPoint; - } - - /** - * Transform the given dimension from this node's local coordinate system to - * the global coordinate system. Note that this will modify the dimension - * parameter. - * - * @param localDimension dimension in local coordinate system to be - * transformed. - * @return dimension in global coordinates - */ - public Dimension2D localToGlobal(final Dimension2D localDimension) { - PNode n = this; - while (n != null) { - n.localToParent(localDimension); - n = n.parent; - } - return localDimension; - } - - /** - * Transform the given rectangle from this node's local coordinate system to - * the global coordinate system. Note that this will modify the rectangle - * parameter. - * - * @param localRectangle rectangle in local coordinate system to be - * transformed. - * @return rectangle in global coordinates - */ - public Rectangle2D localToGlobal(final Rectangle2D localRectangle) { - PNode n = this; - while (n != null) { - n.localToParent(localRectangle); - n = n.parent; - } - return localRectangle; - } - - /** - * Transform the given point from global coordinates to this node's local - * coordinate system. Note that this will modify the point parameter. - * - * @param globalPoint point in global coordinates to be transformed. - * @return point in this node's local coordinate system. - */ - public Point2D globalToLocal(final Point2D globalPoint) { - final PAffineTransform globalTransform = computeGlobalTransform(this); - return globalTransform.inverseTransform(globalPoint, globalPoint); - } - - private PAffineTransform computeGlobalTransform(final PNode node) { - if (node == null) { - return new PAffineTransform(); - } - - final PAffineTransform parentGlobalTransform = computeGlobalTransform(node.parent); - if (node.transform != null) { - parentGlobalTransform.concatenate(node.transform); - } - return parentGlobalTransform; - } - - /** - * Transform the given dimension from global coordinates to this node's - * local coordinate system. Note that this will modify the dimension - * parameter. - * - * @param globalDimension dimension in global coordinates to be transformed. - * @return dimension in this node's local coordinate system. - */ - public Dimension2D globalToLocal(final Dimension2D globalDimension) { - if (parent != null) { - parent.globalToLocal(globalDimension); - } - return parentToLocal(globalDimension); - } - - /** - * Transform the given rectangle from global coordinates to this node's - * local coordinate system. Note that this will modify the rectangle - * parameter. - * - * @param globalRectangle rectangle in global coordinates to be transformed. - * @return rectangle in this node's local coordinate system. - */ - public Rectangle2D globalToLocal(final Rectangle2D globalRectangle) { - if (parent != null) { - parent.globalToLocal(globalRectangle); - } - return parentToLocal(globalRectangle); - } - - /** - * Return the transform that converts local coordinates at this node to the - * global coordinate system. - * - * @param dest PAffineTransform to transform to global coordinates - * @return The concatenation of transforms from the top node down to this - * node. - */ - public PAffineTransform getLocalToGlobalTransform(final PAffineTransform dest) { - PAffineTransform result = dest; - if (parent != null) { - result = parent.getLocalToGlobalTransform(result); - if (transform != null) { - result.concatenate(transform); - } - } - else if (dest == null) { - result = getTransform(); - } - else if (transform != null) { - result.setTransform(transform); - } - else { - result.setToIdentity(); - } - - return result; - } - - /** - * Return the transform that converts global coordinates to local - * coordinates of this node. - * - * @param dest PAffineTransform to transform from global to local - * - * @return The inverse of the concatenation of transforms from the root down - * to this node. - */ - public PAffineTransform getGlobalToLocalTransform(final PAffineTransform dest) { - PAffineTransform result = getLocalToGlobalTransform(dest); - try { - result.setTransform(result.createInverse()); - } - catch (final NoninvertibleTransformException e) { - throw new PAffineTransformException(e, result); - } - return result; - } - - // **************************************************************** - // Event Listeners - Methods for adding and removing event listeners - // from a node. - // - // Here methods are provided to add property change listeners and - // input event listeners. The property change listeners are notified - // when certain properties of this node change, and the input event - // listeners are notified when the nodes receives new key and mouse - // events. - // **************************************************************** - - /** - * Return the list of event listeners associated with this node. - * - * @return event listener list or null - */ - public EventListenerList getListenerList() { - return listenerList; - } - - /** - * Adds the specified input event listener to receive input events from this - * node. - * - * @param listener the new input listener - */ - public void addInputEventListener(final PInputEventListener listener) { - if (listenerList == null) { - listenerList = new EventListenerList(); - } - getListenerList().add(PInputEventListener.class, listener); - } - - /** - * Removes the specified input event listener so that it no longer receives - * input events from this node. - * - * @param listener the input listener to remove - */ - public void removeInputEventListener(final PInputEventListener listener) { - if (listenerList == null) { - return; - } - getListenerList().remove(PInputEventListener.class, listener); - if (listenerList.getListenerCount() == 0) { - listenerList = null; - } - } - - /** - * Add a PropertyChangeListener to the listener list. The listener is - * registered for all properties. See the fields in PNode and subclasses - * that start with PROPERTY_ to find out which properties exist. - * - * @param listener the PropertyChangeListener to be added - */ - public void addPropertyChangeListener(final PropertyChangeListener listener) { - if (changeSupport == null) { - changeSupport = new SwingPropertyChangeSupport(this); - } - changeSupport.addPropertyChangeListener(listener); - } - - /** - * Add a PropertyChangeListener for a specific property. The listener will - * be invoked only when a call on firePropertyChange names that specific - * property. See the fields in PNode and subclasses that start with - * PROPERTY_ to find out which properties are supported. - * - * @param propertyName The name of the property to listen on. - * @param listener the PropertyChangeListener to be added - */ - public void addPropertyChangeListener(final String propertyName, final PropertyChangeListener listener) { - if (listener == null) { - return; - } - if (changeSupport == null) { - changeSupport = new SwingPropertyChangeSupport(this); - } - changeSupport.addPropertyChangeListener(propertyName, listener); - } - - /** - * Remove a PropertyChangeListener from the listener list. This removes a - * PropertyChangeListener that was registered for all properties. - * - * @param listener the PropertyChangeListener to be removed - */ - public void removePropertyChangeListener(final PropertyChangeListener listener) { - if (changeSupport != null) { - changeSupport.removePropertyChangeListener(listener); - } - } - - /** - * Remove a PropertyChangeListener for a specific property. - * - * @param propertyName the name of the property that was listened on. - * @param listener the PropertyChangeListener to be removed - */ - public void removePropertyChangeListener(final String propertyName, final PropertyChangeListener listener) { - if (listener == null) { - return; - } - if (changeSupport == null) { - return; - } - changeSupport.removePropertyChangeListener(propertyName, listener); - } - - /** - * Return an array of all the property change listeners added to this node. - *

- * If some listeners have been added with a named property, then - * the returned array will be a mixture of PropertyChangeListeners - * and PropertyChangeListenerProxys. If the calling - * method is interested in distinguishing the listeners then it must - * test each element to see if it is a PropertyChangeListenerProxy, - * perform the cast, and examine the parameter. - * - *

-     * PropertyChangeListener[] listeners = bean.getPropertyChangeListeners();
-     * for (int i = 0; i < listeners.length; i++) {
-     *   if (listeners[i] instanceof PropertyChangeListenerProxy) {
-     *     PropertyChangeListenerProxy proxy = (PropertyChangeListenerProxy) listeners[i];
-     *     if (proxy.getPropertyName().equals("foo")) {
-     *       // proxy is a PropertyChangeListener which was associated
-     *       // with the property named "foo"
-     *     }
-     *   }
-     * }
-     *
- * - * @since 3.0.1 - * @return all of the PropertyChangeListeners added or an - * empty array if no listeners have been added - */ - public PropertyChangeListener[] getPropertyChangeListeners() { - if (changeSupport == null) { - return new PropertyChangeListener[0]; - } - return changeSupport.getPropertyChangeListeners(); - } - - /** - * Return an array of all the property change listeners which have been - * associated with the named property. - * - * @since 3.0.1 - * @param propertyName the name of the property being listened to - * @return all of the PropertyChangeListeners associated with - * the named property. If no such listeners have been added, - * or if propertyName is null, an empty array is - * returned. - */ - public PropertyChangeListener[] getPropertyChangeListeners(final String propertyName) { - if (changeSupport == null) { - return new PropertyChangeListener[0]; - } - return changeSupport.getPropertyChangeListeners(propertyName); - } - - /** - * Return the propertyChangeParentMask that determines which property change - * events are forwared to this nodes parent so that its property change - * listeners will also be notified. - * - * @return mask used for deciding whether to bubble property changes up to - * parent - */ - public int getPropertyChangeParentMask() { - return propertyChangeParentMask; - } - - /** - * Set the propertyChangeParentMask that determines which property change - * events are forwared to this nodes parent so that its property change - * listeners will also be notified. - * - * @param propertyChangeParentMask new mask for property change bubble up - */ - public void setPropertyChangeParentMask(final int propertyChangeParentMask) { - this.propertyChangeParentMask = propertyChangeParentMask; - } - - /** - * Report a bound property update to any registered listeners. No event is - * fired if old and new are equal and non-null. If the propertyCode exists - * in this node's propertyChangeParentMask then a property change event will - * also be fired on this nodes parent. - * - * @param propertyCode The code of the property changed. - * @param propertyName The name of the property that was changed. - * @param oldValue The old value of the property. - * @param newValue The new value of the property. - */ - protected void firePropertyChange(final int propertyCode, final String propertyName, final Object oldValue, - final Object newValue) { - PropertyChangeEvent event = null; - - if (changeSupport != null) { - event = new PropertyChangeEvent(this, propertyName, oldValue, newValue); - changeSupport.firePropertyChange(event); - } - if (parent != null && (propertyCode & propertyChangeParentMask) != 0) { - if (event == null) { - event = new PropertyChangeEvent(this, propertyName, oldValue, newValue); - } - parent.fireChildPropertyChange(event, propertyCode); - } - } - - /** - * Called by child node to forward property change events up the node tree - * so that property change listeners registered with this node will be - * notified of property changes of its children nodes. For performance - * reason only propertyCodes listed in the propertyChangeParentMask are - * forwarded. - * - * @param event The property change event containing source node and changed - * values. - * @param propertyCode The code of the property changed. - */ - protected void fireChildPropertyChange(final PropertyChangeEvent event, final int propertyCode) { - if (changeSupport != null) { - changeSupport.firePropertyChange(event); - } - if (parent != null && (propertyCode & propertyChangeParentMask) != 0) { - parent.fireChildPropertyChange(event, propertyCode); - } - } - - // **************************************************************** - // Bounds Geometry - Methods for setting and querying the bounds - // of this node. - // - // The bounds of a node store the node's position and size in - // the nodes local coordinate system. Many node subclasses will need - // to override the setBounds method so that they can update their - // internal state appropriately. See PPath for an example. - // - // Since the bounds are stored in the local coordinate system - // they WILL NOT change if the node is scaled, translated, or rotated. - // - // The bounds may be accessed with either getBounds, or - // getBoundsReference. The former returns a copy of the bounds - // the latter returns a reference to the nodes bounds that should - // normally not be modified. If a node is marked as volatile then - // it may modify its bounds before returning them from getBoundsReference, - // otherwise it may not. - // **************************************************************** - - /** - * Return a copy of this node's bounds. These bounds are stored in the local - * coordinate system of this node and do not include the bounds of any of - * this node's children. - * - * @return copy of this node's local bounds - */ - public PBounds getBounds() { - return (PBounds) getBoundsReference().clone(); - } - - /** - * Return a direct reference to this node's bounds. These bounds are stored - * in the local coordinate system of this node and do not include the bounds - * of any of this node's children. The value returned should not be - * modified. - * - * @return direct reference to local bounds - */ - public PBounds getBoundsReference() { - return bounds; - } - - /** - * Notify this node that you will begin to repeatedly call setBounds - * . When you - * are done call endResizeBounds to let the node know that you - * are done. - */ - public void startResizeBounds() { - } - - /** - * Notify this node that you have finished a resize bounds sequence. - */ - public void endResizeBounds() { - } - - /** - * Set's this node's bounds left position, leaving y, width, and height - * unchanged. - * - * @param x new x position of bounds - * - * @return whether the change was successful - */ - public boolean setX(final double x) { - return setBounds(x, getY(), getWidth(), getHeight()); - } - - /** - * Set's this node's bounds top position, leaving x, width, and height - * unchanged. - * - * @param y new y position of bounds - * - * @return whether the change was successful - */ - public boolean setY(final double y) { - return setBounds(getX(), y, getWidth(), getHeight()); - } - - /** - * Set's this node's bounds width, leaving x, y, and height unchanged. - * - * @param width new width position of bounds - * - * @return whether the change was successful - */ - public boolean setWidth(final double width) { - return setBounds(getX(), getY(), width, getHeight()); - } - - /** - * Set's this node's bounds height, leaving x, y, and width unchanged. - * - * @param height new height position of bounds - * - * @return whether the change was successful - */ - public boolean setHeight(final double height) { - return setBounds(getX(), getY(), getWidth(), height); - } - - /** - * Set the bounds of this node to the given value. These bounds are stored - * in the local coordinate system of this node. - * - * @param newBounds bounds to apply to this node - * - * @return true if the bounds changed. - */ - public boolean setBounds(final Rectangle2D newBounds) { - return setBounds(newBounds.getX(), newBounds.getY(), newBounds.getWidth(), newBounds.getHeight()); - } - - /** - * Set the bounds of this node to the given position and size. These bounds - * are stored in the local coordinate system of this node. - * - * If the width or height is less then or equal to zero then the bound's - * empty bit will be set to true. - * - * Subclasses must call the super.setBounds() method. - * - * @param x x position of bounds - * @param y y position of bounds - * @param width width to apply to the bounds - * @param height height to apply to the bounds - * - * @return true if the bounds changed. - */ - public boolean setBounds(final double x, final double y, final double width, final double height) { - if (bounds.x != x || bounds.y != y || bounds.width != width || bounds.height != height) { - bounds.setRect(x, y, width, height); - - if (width <= 0 || height <= 0) { - bounds.reset(); - } - - internalUpdateBounds(x, y, width, height); - invalidatePaint(); - signalBoundsChanged(); - return true; - } - // Don't put any invalidating code here or else nodes with volatile - // bounds will - // create a soft infinite loop (calling Swing.invokeLater()) when they - // validate - // their bounds. - return false; - } - - /** - * Gives nodes a chance to update their internal structure before bounds - * changed notifications are sent. When this message is recived the nodes - * bounds field will contain the new value. - * - * See PPath for an example that uses this method. - * - * @param x x position of bounds - * @param y y position of bounds - * @param width width to apply to the bounds - * @param height height to apply to the bounds - */ - protected void internalUpdateBounds(final double x, final double y, final double width, final double height) { - } - - /** - * Set the empty bit of this bounds to true. - */ - public void resetBounds() { - setBounds(0, 0, 0, 0); - } - - /** - * Return the x position (in local coords) of this node's bounds. - * - * @return local x position of bounds - */ - public double getX() { - return getBoundsReference().getX(); - } - - /** - * Return the y position (in local coords) of this node's bounds. - * - * @return local y position of bounds - */ - public double getY() { - return getBoundsReference().getY(); - } - - /** - * Return the width (in local coords) of this node's bounds. - * - * @return local width of bounds - */ - public double getWidth() { - return getBoundsReference().getWidth(); - } - - /** - * Return the height (in local coords) of this node's bounds. - * - * @return local width of bounds - */ - public double getHeight() { - return getBoundsReference().getHeight(); - } - - /** - * Return a copy of the bounds of this node in the global coordinate system. - * - * @return the bounds in global coordinate system. - */ - public PBounds getGlobalBounds() { - return (PBounds) localToGlobal(getBounds()); - } - - /** - * Center the bounds of this node so that they are centered on the given - * point specified on the local coordinates of this node. Note that this - * method will modify the nodes bounds, while centerFullBoundsOnPoint will - * modify the nodes transform. - * - * @param localX x position of point around which to center bounds - * @param localY y position of point around which to center bounds - * - * @return true if the bounds changed. - */ - public boolean centerBoundsOnPoint(final double localX, final double localY) { - final double dx = localX - bounds.getCenterX(); - final double dy = localY - bounds.getCenterY(); - return setBounds(bounds.x + dx, bounds.y + dy, bounds.width, bounds.height); - } - - /** - * Center the full bounds of this node so that they are centered on the - * given point specified on the local coordinates of this nodes parent. Note - * that this method will modify the nodes transform, while - * centerBoundsOnPoint will modify the nodes bounds. - * - * @param parentX x position around which to center full bounds - * @param parentY y position around which to center full bounds - */ - public void centerFullBoundsOnPoint(final double parentX, final double parentY) { - final double dx = parentX - getFullBoundsReference().getCenterX(); - final double dy = parentY - getFullBoundsReference().getCenterY(); - offset(dx, dy); - } - - /** - * Return true if this node intersects the given rectangle specified in - * local bounds. If the geometry of this node is complex this method can - * become expensive, it is therefore recommended that - * fullIntersects is used for quick rejects before calling this - * method. - * - * @param localBounds the bounds to test for intersection against - * @return true if the given rectangle intersects this nodes geometry. - */ - public boolean intersects(final Rectangle2D localBounds) { - if (localBounds == null) { - return true; - } - return getBoundsReference().intersects(localBounds); - } - - // **************************************************************** - // Full Bounds - Methods for computing and querying the - // full bounds of this node. - // - // The full bounds of a node store the nodes bounds - // together with the union of the bounds of all the - // node's descendants. The full bounds are stored in the parent - // coordinate system of this node, the full bounds DOES change - // when you translate, scale, or rotate this node. - // - // The full bounds may be accessed with either getFullBounds, or - // getFullBoundsReference. The former returns a copy of the full bounds - // the latter returns a reference to the node's full bounds that should - // not be modified. - // **************************************************************** - - /** - * Return a copy of this node's full bounds. These bounds are stored in the - * parent coordinate system of this node and they include the union of this - * node's bounds and all the bounds of it's descendants. - * - * @return a copy of this node's full bounds. - */ - public PBounds getFullBounds() { - return (PBounds) getFullBoundsReference().clone(); - } - - /** - * Return a reference to this node's full bounds cache. These bounds are - * stored in the parent coordinate system of this node and they include the - * union of this node's bounds and all the bounds of it's descendants. The - * bounds returned by this method should not be modified. - * - * @return a reference to this node's full bounds cache. - */ - public PBounds getFullBoundsReference() { - validateFullBounds(); - return fullBoundsCache; - } - - /** - * Compute and return the full bounds of this node. If the dstBounds - * parameter is not null then it will be used to return the results instead - * of creating a new PBounds. - * - * @param dstBounds if not null the new bounds will be stored here - * @return the full bounds in the parent coordinate system of this node - */ - public PBounds computeFullBounds(final PBounds dstBounds) { - final PBounds result = getUnionOfChildrenBounds(dstBounds); - result.add(getBoundsReference()); - localToParent(result); - return result; - } - - /** - * Compute and return the union of the full bounds of all the children of - * this node. If the dstBounds parameter is not null then it will be used to - * return the results instead of creating a new PBounds. - * - * @param dstBounds if not null the new bounds will be stored here - * @return union of children bounds - */ - public PBounds getUnionOfChildrenBounds(final PBounds dstBounds) { - PBounds resultBounds; - if (dstBounds == null) { - resultBounds = new PBounds(); - } - else { - resultBounds = dstBounds; - resultBounds.resetToZero(); - } - - final int count = getChildrenCount(); - for (int i = 0; i < count; i++) { - final PNode each = (PNode) children.get(i); - resultBounds.add(each.getFullBoundsReference()); - } - - return resultBounds; - } - - /** - * Return a copy of the full bounds of this node in the global coordinate - * system. - * - * @return the full bounds in global coordinate system. - */ - public PBounds getGlobalFullBounds() { - final PBounds b = getFullBounds(); - if (parent != null) { - parent.localToGlobal(b); - } - return b; - } - - /** - * Return true if the full bounds of this node intersects with the specified - * bounds. - * - * @param parentBounds the bounds to test for intersection against - * (specified in parent's coordinate system) - * @return true if this nodes full bounds intersect the given bounds. - */ - public boolean fullIntersects(final Rectangle2D parentBounds) { - if (parentBounds == null) { - return true; - } - return getFullBoundsReference().intersects(parentBounds); - } - - // **************************************************************** - // Bounds Damage Management - Methods used to invalidate and validate - // the bounds of nodes. - // **************************************************************** - - /** - * Return true if this nodes bounds may change at any time. The default - * behavior is to return false, subclasses that override this method to - * return true should also override getBoundsReference() and compute their - * volatile bounds there before returning the reference. - * - * @return true if this node has volatile bounds - */ - protected boolean getBoundsVolatile() { - return false; - } - - /** - * Return true if this node has a child with volatile bounds. - * - * @return true if this node has a child with volatile bounds - */ - protected boolean getChildBoundsVolatile() { - return childBoundsVolatile; - } - - /** - * Set if this node has a child with volatile bounds. This should normally - * be managed automatically by the bounds validation process. - * - * @param childBoundsVolatile true if this node has a descendant with - * volatile bounds - */ - protected void setChildBoundsVolatile(final boolean childBoundsVolatile) { - this.childBoundsVolatile = childBoundsVolatile; - } - - /** - * Return true if this node's bounds have recently changed. This flag will - * be reset on the next call of validateFullBounds. - * - * @return true if this node's bounds have changed. - */ - protected boolean getBoundsChanged() { - return boundsChanged; - } - - /** - * Set the bounds chnaged flag. This flag will be reset on the next call of - * validateFullBounds. - * - * @param boundsChanged true if this nodes bounds have changed. - */ - protected void setBoundsChanged(final boolean boundsChanged) { - this.boundsChanged = boundsChanged; - } - - /** - * Return true if the full bounds of this node are invalid. This means that - * the full bounds of this node have changed and need to be recomputed. - * - * @return true if the full bounds of this node are invalid - */ - protected boolean getFullBoundsInvalid() { - return fullBoundsInvalid; - } - - /** - * Set the full bounds invalid flag. This flag is set when the full bounds - * of this node need to be recomputed as is the case when this node is - * transformed or when one of this node's children changes geometry. - * - * @param fullBoundsInvalid true=invalid, false=valid - */ - protected void setFullBoundsInvalid(final boolean fullBoundsInvalid) { - this.fullBoundsInvalid = fullBoundsInvalid; - } - - /** - * Return true if one of this node's descendants has invalid bounds. - * - * @return whether child bounds are invalid - */ - protected boolean getChildBoundsInvalid() { - return childBoundsInvalid; - } - - /** - * Set the flag indicating that one of this node's descendants has invalid - * bounds. - * - * @param childBoundsInvalid true=invalid, false=valid - */ - protected void setChildBoundsInvalid(final boolean childBoundsInvalid) { - this.childBoundsInvalid = childBoundsInvalid; - } - - /** - * This method should be called when the bounds of this node are changed. It - * invalidates the full bounds of this node, and also notifies each of this - * nodes children that their parent's bounds have changed. As a result of - * this method getting called this nodes layoutChildren will be called. - */ - public void signalBoundsChanged() { - invalidateFullBounds(); - setBoundsChanged(true); - firePropertyChange(PROPERTY_CODE_BOUNDS, PROPERTY_BOUNDS, null, bounds); - - final int count = getChildrenCount(); - for (int i = 0; i < count; i++) { - final PNode each = (PNode) children.get(i); - each.parentBoundsChanged(); - } - } - - /** - * Invalidate this node's layout, so that later layoutChildren will get - * called. - */ - public void invalidateLayout() { - invalidateFullBounds(); - } - - /** - * A notification that the bounds of this node's parent have changed. - */ - protected void parentBoundsChanged() { - } - - /** - * Invalidates the full bounds of this node, and sets the child bounds - * invalid flag on each of this node's ancestors. - */ - public void invalidateFullBounds() { - setFullBoundsInvalid(true); - - PNode n = parent; - while (n != null && !n.getChildBoundsInvalid()) { - n.setChildBoundsInvalid(true); - n = n.parent; - } - - if (SCENE_GRAPH_DELEGATE != null) { - SCENE_GRAPH_DELEGATE.nodeFullBoundsInvalidated(this); - } - } - - /** - * This method is called to validate the bounds of this node and all of its - * descendants. It returns true if this nodes bounds or the bounds of any of - * its descendants are marked as volatile. - * - * @return true if this node or any of its descendants have volatile bounds - */ - protected boolean validateFullBounds() { - final boolean boundsVolatile = getBoundsVolatile(); - - // 1. Only compute new bounds if invalid flags are set. - if (fullBoundsInvalid || childBoundsInvalid || boundsVolatile || childBoundsVolatile) { - - // 2. If my bounds are volatile and they have not been changed then - // signal a change. - // - // For most cases this will do nothing, but if a nodes bounds depend - // on its model, then - // validate bounds has the responsibility of making the bounds match - // the models value. - // For example PPaths validateBounds method makes sure that the - // bounds are equal to the - // bounds of the GeneralPath model. - if (boundsVolatile && !boundsChanged) { - signalBoundsChanged(); - } - - - // 3. If the bounds of on of my decendents are invalidate then - // validate the bounds of all of my children. - if (childBoundsInvalid || childBoundsVolatile) { - childBoundsVolatile = false; - final int count = getChildrenCount(); - for (int i = 0; i < count; i++) { - final PNode each = (PNode) children.get(i); - childBoundsVolatile |= each.validateFullBounds(); - } - } - - // 4. Now that my children's bounds are valid and my own bounds are - // valid run any layout algorithm here. Note that if you try to - // layout volatile - // children piccolo will most likely start a "soft" infinite loop. - // It won't freeze - // your program, but it will make an infinite number of calls to - // SwingUtilities - // invoke later. You don't want to do that. - layoutChildren(); - - // 5. If the full bounds cache is invalid then recompute the full - // bounds cache here after our own bounds and the children's bounds - // have been computed above. - if (fullBoundsInvalid) { - final double oldX = fullBoundsCache.x; - final double oldY = fullBoundsCache.y; - final double oldWidth = fullBoundsCache.width; - final double oldHeight = fullBoundsCache.height; - final boolean oldEmpty = fullBoundsCache.isEmpty(); - - // 6. This will call getFullBoundsReference on all of the - // children. So if the above - // layoutChildren method changed the bounds of any of the - // children they will be - // validated again here. - fullBoundsCache = computeFullBounds(fullBoundsCache); - - final boolean fullBoundsChanged = fullBoundsCache.x != oldX || fullBoundsCache.y != oldY - || fullBoundsCache.width != oldWidth || fullBoundsCache.height != oldHeight - || fullBoundsCache.isEmpty() != oldEmpty; - - // 7. If the new full bounds cache differs from the previous - // cache then - // tell our parent to invalidate their full bounds. This is how - // bounds changes - // deep in the tree percolate up. - if (fullBoundsChanged) { - if (parent != null) { - parent.invalidateFullBounds(); - } - firePropertyChange(PROPERTY_CODE_FULL_BOUNDS, PROPERTY_FULL_BOUNDS, null, fullBoundsCache); - - // 8. If our paint was invalid make sure to repaint our old - // full bounds. The - // new bounds will be computed later in the validatePaint - // pass. - if (paintInvalid && !oldEmpty) { - TEMP_REPAINT_BOUNDS.setRect(oldX, oldY, oldWidth, oldHeight); - repaintFrom(TEMP_REPAINT_BOUNDS, this); - } - } - } - - // 9. Clear the invalid bounds flags. - boundsChanged = false; - fullBoundsInvalid = false; - childBoundsInvalid = false; - } - - return boundsVolatile || childBoundsVolatile; - } - - /** - * Nodes that apply layout constraints to their children should override - * this method and do the layout there. - */ - protected void layoutChildren() { - } - - // **************************************************************** - // Node Transform - Methods to manipulate the node's transform. - // - // Each node has a transform that is used to define the nodes - // local coordinate system. IE it is applied before picking and - // rendering the node. - // - // The usual way to move nodes about on the canvas is to manipulate - // this transform, as opposed to changing the bounds of the - // node. - // - // Since this transform defines the local coordinate system of this - // node the following methods with affect the global position both - // this node and all of its descendants. - // **************************************************************** - - /** - * Returns the rotation applied by this node's transform in radians. This - * rotation affects this node and all its descendants. The value returned - * will be between 0 and 2pi radians. - * - * @return rotation in radians. - */ - public double getRotation() { - if (transform == null) { - return 0; - } - return transform.getRotation(); - } - - /** - * Sets the rotation of this nodes transform in radians. This will affect - * this node and all its descendents. - * - * @param theta rotation in radians - */ - public void setRotation(final double theta) { - rotate(theta - getRotation()); - } - - /** - * Rotates this node by theta (in radians) about the 0,0 point. This will - * affect this node and all its descendants. - * - * @param theta the amount to rotate by in radians - */ - public void rotate(final double theta) { - rotateAboutPoint(theta, 0, 0); - } - - /** - * Rotates this node by theta (in radians), and then translates the node so - * that the x, y position of its fullBounds stays constant. - * - * @param theta the amount to rotate by in radians - */ - public void rotateInPlace(final double theta) { - PBounds b = getFullBoundsReference(); - final double px = b.x; - final double py = b.y; - rotateAboutPoint(theta, 0, 0); - b = getFullBoundsReference(); - offset(px - b.x, py - b.y); - } - - /** - * Rotates this node by theta (in radians) about the given point. This will - * affect this node and all its descendants. - * - * @param theta the amount to rotate by in radians - * @param point the point about which to rotate - */ - public void rotateAboutPoint(final double theta, final Point2D point) { - rotateAboutPoint(theta, point.getX(), point.getY()); - } - - /** - * Rotates this node by theta (in radians) about the given point. This will - * affect this node and all its descendants. - * - * @param theta the amount to rotate by in radians - * @param x the x coordinate of the point around which to rotate - * @param y the y coordinate of the point around which to rotate - */ - public void rotateAboutPoint(final double theta, final double x, final double y) { - getTransformReference(true).rotate(theta, x, y); - invalidatePaint(); - invalidateFullBounds(); - firePropertyChange(PROPERTY_CODE_TRANSFORM, PROPERTY_TRANSFORM, null, transform); - } - - /** - * Return the total amount of rotation applied to this node by its own - * transform together with the transforms of all its ancestors. The value - * returned will be between 0 and 2pi radians. - * - * @return the total amount of rotation applied to this node in radians - */ - public double getGlobalRotation() { - return getLocalToGlobalTransform(null).getRotation(); - } - - /** - * Set the global rotation (in radians) of this node. This is implemented by - * rotating this nodes transform the required amount so that the nodes - * global rotation is as requested. - * - * @param theta the amount to rotate by in radians relative to the global - * coordinate system. - */ - public void setGlobalRotation(final double theta) { - if (parent != null) { - setRotation(theta - parent.getGlobalRotation()); - } - else { - setRotation(theta); - } - } - - /** - * Return the scale applied by this node's transform. The scale is effecting - * this node and all its descendants. - * - * @return scale applied by this nodes transform. - */ - public double getScale() { - if (transform == null) { - return 1; - } - return transform.getScale(); - } - - /** - * Set the scale of this node's transform. The scale will affect this node - * and all its descendants. - * - * @param scale the scale to set the transform to - */ - public void setScale(final double scale) { - if (scale == 0) { - throw new RuntimeException("Can't set scale to 0"); - } - scale(scale / getScale()); - } - - /** - * Scale this nodes transform by the given amount. This will affect this - * node and all of its descendants. - * - * @param scale the amount to scale by - */ - public void scale(final double scale) { - scaleAboutPoint(scale, 0, 0); - } - - /** - * Scale this nodes transform by the given amount about the specified point. - * This will affect this node and all of its descendants. - * - * @param scale the amount to scale by - * @param point the point to scale about - */ - public void scaleAboutPoint(final double scale, final Point2D point) { - scaleAboutPoint(scale, point.getX(), point.getY()); - } - - /** - * Scale this nodes transform by the given amount about the specified point. - * This will affect this node and all of its descendants. - * - * @param scale the amount to scale by - * @param x the x coordinate of the point around which to scale - * @param y the y coordinate of the point around which to scale - */ - public void scaleAboutPoint(final double scale, final double x, final double y) { - getTransformReference(true).scaleAboutPoint(scale, x, y); - invalidatePaint(); - invalidateFullBounds(); - firePropertyChange(PROPERTY_CODE_TRANSFORM, PROPERTY_TRANSFORM, null, transform); - } - - /** - * Return the global scale that is being applied to this node by its - * transform together with the transforms of all its ancestors. - * - * @return global scale of this node - */ - public double getGlobalScale() { - return getLocalToGlobalTransform(null).getScale(); - } - - /** - * Set the global scale of this node. This is implemented by scaling this - * nodes transform the required amount so that the nodes global scale is as - * requested. - * - * @param scale the desired global scale - */ - public void setGlobalScale(final double scale) { - if (parent != null) { - setScale(scale / parent.getGlobalScale()); - } - else { - setScale(scale); - } - } - - /** - * Returns the x offset of this node as applied by its transform. - * - * @return x offset of this node as applied by its transform - */ - public double getXOffset() { - if (transform == null) { - return 0; - } - return transform.getTranslateX(); - } - - /** - * Returns the y offset of this node as applied by its transform. - * - * @return y offset of this node as applied by its transform - */ - public double getYOffset() { - if (transform == null) { - return 0; - } - return transform.getTranslateY(); - } - - /** - * Return the offset that is being applied to this node by its transform. - * This offset effects this node and all of its descendants and is specified - * in the parent coordinate system. This returns the values that are in the - * m02 and m12 positions in the affine transform. - * - * @return a point representing the x and y offset - */ - public Point2D getOffset() { - if (transform == null) { - return new Point2D.Double(); - } - return new Point2D.Double(transform.getTranslateX(), transform.getTranslateY()); - } - - /** - * Set the offset that is being applied to this node by its transform. This - * offset effects this node and all of its descendants and is specified in - * the nodes parent coordinate system. This directly sets the values of the - * m02 and m12 positions in the affine transform. Unlike "PNode.translate()" - * it is not effected by the transforms scale. - * - * @param point value of new offset - */ - public void setOffset(final Point2D point) { - setOffset(point.getX(), point.getY()); - } - - /** - * Set the offset that is being applied to this node by its transform. This - * offset effects this node and all of its descendants and is specified in - * the nodes parent coordinate system. This directly sets the values of the - * m02 and m12 positions in the affine transform. Unlike "PNode.translate()" - * it is not effected by the transforms scale. - * - * @param x amount of x offset - * @param y amount of y offset - */ - public void setOffset(final double x, final double y) { - getTransformReference(true).setOffset(x, y); - invalidatePaint(); - invalidateFullBounds(); - firePropertyChange(PROPERTY_CODE_TRANSFORM, PROPERTY_TRANSFORM, null, transform); - } - - /** - * Offset this node relative to the parents coordinate system, and is NOT - * effected by this nodes current scale or rotation. This is implemented by - * directly adding dx to the m02 position and dy to the m12 position in the - * affine transform. - * - * @param dx amount to add to this nodes current x Offset - * @param dy amount to add to this nodes current y Offset - */ - public void offset(final double dx, final double dy) { - getTransformReference(true); - setOffset(transform.getTranslateX() + dx, transform.getTranslateY() + dy); - } - - /** - * Translate this node's transform by the given amount, using the standard - * affine transform translate method. This translation effects this node and - * all of its descendants. - * - * @param dx amount to add to this nodes current x translation - * @param dy amount to add to this nodes current y translation - */ - public void translate(final double dx, final double dy) { - getTransformReference(true).translate(dx, dy); - invalidatePaint(); - invalidateFullBounds(); - firePropertyChange(PROPERTY_CODE_TRANSFORM, PROPERTY_TRANSFORM, null, transform); - } - - /** - * Return the global translation that is being applied to this node by its - * transform together with the transforms of all its ancestors. - * - * @return the global translation applied to this node - */ - public Point2D getGlobalTranslation() { - final Point2D p = getOffset(); - if (parent != null) { - parent.localToGlobal(p); - } - return p; - } - - /** - * Set the global translation of this node. This is implemented by - * translating this nodes transform the required amount so that the nodes - * global scale is as requested. - * - * @param globalPoint the desired global translation - */ - public void setGlobalTranslation(final Point2D globalPoint) { - if (parent != null) { - parent.getGlobalToLocalTransform(null).transform(globalPoint, globalPoint); - } - setOffset(globalPoint); - } - - /** - * Transform this nodes transform by the given transform. - * - * @param aTransform the transform to apply. - */ - public void transformBy(final AffineTransform aTransform) { - getTransformReference(true).concatenate(aTransform); - invalidatePaint(); - invalidateFullBounds(); - firePropertyChange(PROPERTY_CODE_TRANSFORM, PROPERTY_TRANSFORM, null, transform); - } - - /** - * Linearly interpolates between a and b, based on t. Specifically, it - * computes lerp(a, b, t) = a + t*(b - a). This produces a result that - * changes from a (when t = 0) to b (when t = 1). - * - * @param t variable 'time' parameter - * @param a from point - * @param b to Point - * - * @return linear interpolation between and b at time interval t (given as # - * between 0f and 1f) - */ - public static double lerp(final double t, final double a, final double b) { - return a + t * (b - a); - } - - /** - * This will calculate the necessary transform in order to make this node - * appear at a particular position relative to the specified bounding box. - * The source point specifies a point in the unit square (0, 0) - (1, 1) - * that represents an anchor point on the corresponding node to this - * transform. The destination point specifies an anchor point on the - * reference node. The position method then computes the transform that - * results in transforming this node so that the source anchor point - * coincides with the reference anchor point. This can be useful for layout - * algorithms as it is straightforward to position one object relative to - * another. - *

- * For example, If you have two nodes, A and B, and you call - * - *

-     * Point2D srcPt = new Point2D.Double(1.0, 0.0);
-     * Point2D destPt = new Point2D.Double(0.0, 0.0);
-     * A.position(srcPt, destPt, B.getGlobalBounds(), 750, null);
-     * 
- * - * The result is that A will move so that its upper-right corner is at the - * same place as the upper-left corner of B, and the transition will be - * smoothly animated over a period of 750 milliseconds. - * - * @since 1.3 - * @param srcPt The anchor point on this transform's node (normalized to a - * unit square) - * @param destPt The anchor point on destination bounds (normalized to a - * unit square) - * @param destBounds The bounds (in global coordinates) used to calculate - * this transform's node - * @param millis Number of milliseconds over which to perform the animation - * - * @return newly scheduled activity or node if activity could not be - * scheduled - */ - public PActivity animateToRelativePosition(final Point2D srcPt, final Point2D destPt, final Rectangle2D destBounds, - final int millis) { - double srcx, srcy; - double destx, desty; - double dx, dy; - Point2D pt1, pt2; - - if (parent == null) { - return null; - } - else { - // First compute translation amount in global coordinates - final Rectangle2D srcBounds = getGlobalFullBounds(); - srcx = lerp(srcPt.getX(), srcBounds.getX(), srcBounds.getX() + srcBounds.getWidth()); - srcy = lerp(srcPt.getY(), srcBounds.getY(), srcBounds.getY() + srcBounds.getHeight()); - destx = lerp(destPt.getX(), destBounds.getX(), destBounds.getX() + destBounds.getWidth()); - desty = lerp(destPt.getY(), destBounds.getY(), destBounds.getY() + destBounds.getHeight()); - - // Convert vector to local coordinates - pt1 = new Point2D.Double(srcx, srcy); - globalToLocal(pt1); - pt2 = new Point2D.Double(destx, desty); - globalToLocal(pt2); - dx = pt2.getX() - pt1.getX(); - dy = pt2.getY() - pt1.getY(); - - // Finally, animate change - final PAffineTransform at = new PAffineTransform(getTransformReference(true)); - at.translate(dx, dy); - return animateToTransform(at, millis); - } - } - - /** - * Return a copy of the transform associated with this node. - * - * @return copy of this node's transform - */ - public PAffineTransform getTransform() { - if (transform == null) { - return new PAffineTransform(); - } - else { - return (PAffineTransform) transform.clone(); - } - } - - /** - * Return a reference to the transform associated with this node. This - * returned transform should not be modified. PNode transforms are created - * lazily when needed. If you access the transform reference before the - * transform has been created it may return null. The - * createNewTransformIfNull parameter is used to specify that the PNode - * should create a new transform (and assign that transform to the nodes - * local transform variable) instead of returning null. - * - * @param createNewTransformIfNull if the transform has not been - * initialised, should it be? - * - * @return reference to this node's transform - */ - public PAffineTransform getTransformReference(final boolean createNewTransformIfNull) { - if (transform == null && createNewTransformIfNull) { - transform = new PAffineTransform(); - } - return transform; - } - - /** - * Return an inverted copy of the transform associated with this node. - * - * @return inverted copy of this node's transform - */ - public PAffineTransform getInverseTransform() { - if (transform == null) { - return new PAffineTransform(); - } - - try { - return new PAffineTransform(transform.createInverse()); - } - catch (final NoninvertibleTransformException e) { - throw new PAffineTransformException(e, transform); - } - } - - /** - * Set the transform applied to this node. - * - * @param transform the new transform value - */ - public void setTransform(final AffineTransform transform) { - if (transform == null) { - this.transform = null; - } - else { - getTransformReference(true).setTransform(transform); - } - - invalidatePaint(); - invalidateFullBounds(); - firePropertyChange(PROPERTY_CODE_TRANSFORM, PROPERTY_TRANSFORM, null, this.transform); - } - - // **************************************************************** - // Paint Damage Management - Methods used to invalidate the areas of - // the screen that this node appears in so that they will later get - // painted. - // - // Generally you will not need to call these invalidate methods - // when starting out with Piccolo2d because methods such as setPaint - // already automatically call them for you. You will need to call - // them when you start creating your own nodes. - // - // When you do create you own nodes the only method that you will - // normally need to call is invalidatePaint. This method marks the - // nodes as having invalid paint, the root node's UI cycle will then - // later discover this damage and report it to the Java repaint manager. - // - // Repainting is normally done with PNode.invalidatePaint() instead of - // directly calling PNode.repaint() because PNode.repaint() requires - // the nodes bounds to be computed right away. But with invalidatePaint - // the bounds computation can be delayed until the end of the root's UI - // cycle, and this can add up to a bit savings when modifying a - // large number of nodes all at once. - // - // The other methods here will rarely be called except internally - // from the framework. - // **************************************************************** - - /** - * Return true if this nodes paint is invalid, in which case the node needs - * to be repainted. - * - * @return true if this node needs to be repainted - */ - public boolean getPaintInvalid() { - return paintInvalid; - } - - /** - * Mark this node as having invalid paint. If this is set the node will - * later be repainted. Node this method is most often used internally. - * - * @param paintInvalid true if this node should be repainted - */ - public void setPaintInvalid(final boolean paintInvalid) { - this.paintInvalid = paintInvalid; - } - - /** - * Return true if this node has a child with invalid paint. - * - * @return true if this node has a child with invalid paint - */ - public boolean getChildPaintInvalid() { - return childPaintInvalid; - } - - /** - * Mark this node as having a child with invalid paint. - * - * @param childPaintInvalid true if this node has a child with invalid paint - */ - public void setChildPaintInvalid(final boolean childPaintInvalid) { - this.childPaintInvalid = childPaintInvalid; - } - - /** - * Invalidate this node's paint, and mark all of its ancestors as having a - * node with invalid paint. - */ - public void invalidatePaint() { - setPaintInvalid(true); - - PNode n = parent; - while (n != null && !n.getChildPaintInvalid()) { - n.setChildPaintInvalid(true); - n = n.parent; - } - - if (SCENE_GRAPH_DELEGATE != null) { - SCENE_GRAPH_DELEGATE.nodePaintInvalidated(this); - } - } - - /** - * Repaint this node and any of its descendants if they have invalid paint. - */ - public void validateFullPaint() { - if (getPaintInvalid()) { - repaint(); - setPaintInvalid(false); - } - - if (getChildPaintInvalid()) { - final int count = getChildrenCount(); - for (int i = 0; i < count; i++) { - final PNode each = (PNode) children.get(i); - each.validateFullPaint(); - } - setChildPaintInvalid(false); - } - } - - /** - * Mark the area on the screen represented by this nodes full bounds as - * needing a repaint. - */ - public void repaint() { - TEMP_REPAINT_BOUNDS.setRect(getFullBoundsReference()); - repaintFrom(TEMP_REPAINT_BOUNDS, this); - } - - /** - * Pass the given repaint request up the tree, so that any cameras can - * invalidate that region on their associated canvas. - * - * @param localBounds the bounds to repaint - * @param childOrThis if childOrThis does not equal this then this nodes - * transform will be applied to the localBounds param - */ - public void repaintFrom(final PBounds localBounds, final PNode childOrThis) { - if (parent != null) { - if (childOrThis != this) { - localToParent(localBounds); - } - else if (!getVisible()) { - return; - } - parent.repaintFrom(localBounds, this); - } - } - - // **************************************************************** - // Occluding - Methods to support occluding optimisation. Not yet - // complete. - // **************************************************************** - - /** - * Returns whether this node is Opaque. - * - * @param boundary boundary to check and see if this node covers completely. - * - * @return true if opaque - */ - public boolean isOpaque(final Rectangle2D boundary) { - return false; - } - - /** - * Returns whether this node has been flagged as occluded. - * - * @return true if occluded - */ - public boolean getOccluded() { - return occluded; - } - - /** - * Flags this node as occluded. - * - * @param occluded new value for occluded - */ - public void setOccluded(final boolean occluded) { - this.occluded = occluded; - } - - // **************************************************************** - // Painting - Methods for painting this node and its children - // - // Painting is how a node defines its visual representation on the - // screen, and is done in the local coordinate system of the node. - // - // The default painting behavior is to first paint the node, and - // then paint the node's children on top of the node. If a node - // needs wants specialised painting behavior it can override: - // - // paint() - Painting here will happen before the children - // are painted, so the children will be painted on top of painting done - // here. - // paintAfterChildren() - Painting here will happen after the children - // are painted, so it will paint on top of them. - // - // Note that you should not normally need to override fullPaint(). - // - // The visible flag can be used to make a node invisible so that - // it will never get painted. - // **************************************************************** - - /** - * Return true if this node is visible, that is if it will paint itself and - * descendants. - * - * @return true if this node and its descendants are visible. - */ - public boolean getVisible() { - return visible; - } - - /** - * Set the visibility of this node and its descendants. - * - * @param isVisible true if this node and its descendants are visible - */ - public void setVisible(final boolean isVisible) { - if (getVisible() != isVisible) { - if (!isVisible) { - repaint(); - } - visible = isVisible; - firePropertyChange(PROPERTY_CODE_VISIBLE, PROPERTY_VISIBLE, null, null); - invalidatePaint(); - } - } - - /** - * Return the paint used while painting this node. This value may be null. - * - * @return the paint used while painting this node. - */ - public Paint getPaint() { - return paint; - } - - /** - * Set the paint used to paint this node, which may be null. - * - * @param newPaint paint that this node should use when painting itself. - */ - public void setPaint(final Paint newPaint) { - if (paint == newPaint) { - return; - } - - final Paint oldPaint = paint; - paint = newPaint; - invalidatePaint(); - firePropertyChange(PROPERTY_CODE_PAINT, PROPERTY_PAINT, oldPaint, paint); - } - - /** - * Return the transparency used when painting this node. Note that this - * transparency is also applied to all of the node's descendants. - * - * @return how transparent this node is 0f = completely transparent, 1f = - * completely opaque - */ - public float getTransparency() { - return transparency; - } - - /** - * Set the transparency used to paint this node. Note that this transparency - * applies to this node and all of its descendants. - * - * @param newTransparency transparency value for this node. 0f = fully - * transparent, 1f = fully opaque - */ - public void setTransparency(final float newTransparency) { - if (Math.abs(transparency - newTransparency) > TRANSPARENCY_RESOLUTION) { - final float oldTransparency = transparency; - transparency = newTransparency; - invalidatePaint(); - firePropertyChange(PROPERTY_CODE_TRANSPARENCY, PROPERTY_TRANSPARENCY, new Float(oldTransparency), - new Float(newTransparency)); - } - } - - /** - * Paint this node behind any of its children nodes. Subclasses that define - * a different appearance should override this method and paint themselves - * there. - * - * @param paintContext the paint context to use for painting the node - */ - protected void paint(final PPaintContext paintContext) { - if (paint != null) { - final Graphics2D g2 = paintContext.getGraphics(); - g2.setPaint(paint); - g2.fill(getBoundsReference()); - } - } - - /** - * Paint this node and all of its descendants. Most subclasses do not need - * to override this method, they should override paint or - * paintAfterChildren instead. - * - * @param paintContext the paint context to use for painting this node and - * its children - */ - public void fullPaint(final PPaintContext paintContext) { - if (getVisible() && fullIntersects(paintContext.getLocalClip())) { - paintContext.pushTransform(transform); - paintContext.pushTransparency(transparency); - - if (!getOccluded()) { - paint(paintContext); - } - - final int count = getChildrenCount(); - for (int i = 0; i < count; i++) { - final PNode each = (PNode) children.get(i); - each.fullPaint(paintContext); - } - - paintAfterChildren(paintContext); - - paintContext.popTransparency(transparency); - paintContext.popTransform(transform); - } - } - - /** - * Subclasses that wish to do additional painting after their children are - * painted should override this method and do that painting here. - * - * @param paintContext the paint context to sue for painting after the - * children are painted - */ - protected void paintAfterChildren(final PPaintContext paintContext) { - } - - /** - * Return a new Image representing this node and all of its children. The - * image size will be equal to the size of this nodes full bounds. - * - * @return a new image representing this node and its descendants - */ - public Image toImage() { - final PBounds b = getFullBoundsReference(); - return toImage((int) Math.ceil(b.getWidth()), (int) Math.ceil(b.getHeight()), null); - } - - /** - * Return a new Image of the requested size representing this node and all - * of its children. If backGroundPaint is null the resulting image will have - * transparent regions, otherwise those regions will be filled with the - * backgroundPaint. - * - * @param width pixel width of the resulting image - * @param height pixel height of the resulting image - * @param backgroundPaint paint to fill the image with before drawing this - * node, may be null - * - * @return a new image representing this node and its descendants - */ - public Image toImage(final int width, final int height, final Paint backgroundPaint) { - BufferedImage result; - - if (GraphicsEnvironment.isHeadless()) { - result = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); - } - else { - final GraphicsConfiguration graphicsConfiguration = GraphicsEnvironment.getLocalGraphicsEnvironment() - .getDefaultScreenDevice().getDefaultConfiguration(); - result = graphicsConfiguration.createCompatibleImage(width, height, Transparency.TRANSLUCENT); - } - - return toImage(result, backgroundPaint); - } - - /** - * Paint a representation of this node into the specified buffered image. If - * background, paint is null, then the image will not be filled with a color - * prior to rendering - * - * @param image Image onto which this node will be painted - * @param backGroundPaint will fill background of image with this. May be - * null. - * @return a rendering of this image and its descendants onto the specified - * image - */ - public Image toImage(final BufferedImage image, final Paint backGroundPaint) { - return toImage(image, backGroundPaint, FILL_STRATEGY_ASPECT_FIT); - } - - /** - * Paint a representation of this node into the specified buffered image. If - * background, paint is null, then the image will not be filled with a color - * prior to rendering - * - * @since 1.3 - * @param image Image onto which this node will be painted - * @param backGroundPaint will fill background of image with this. May be - * null. - * @param fillStrategy strategy to use regarding how node will cover the - * image - * @return a rendering of this image and its descendants onto the specified - * image - */ - public Image toImage(final BufferedImage image, final Paint backGroundPaint, final int fillStrategy) { - final int imageWidth = image.getWidth(); - final int imageHeight = image.getHeight(); - final Graphics2D g2 = image.createGraphics(); - - if (backGroundPaint != null) { - g2.setPaint(backGroundPaint); - g2.fillRect(0, 0, imageWidth, imageHeight); - } - g2.setClip(0, 0, imageWidth, imageHeight); - - final PBounds nodeBounds = getFullBounds(); - nodeBounds.expandNearestIntegerDimensions(); - - final double nodeWidth = nodeBounds.getWidth(); - final double nodeHeight = nodeBounds.getHeight(); - - double imageRatio = imageWidth / (imageHeight * 1.0); - double nodeRatio = nodeWidth / nodeHeight; - double scale; - switch (fillStrategy) { - case FILL_STRATEGY_ASPECT_FIT: - // scale the graphics so node's full bounds fit in the imageable - // bounds but aspect ration is retained - - if (nodeRatio <= imageRatio) { - scale = image.getHeight() / nodeHeight; - } - else { - scale = image.getWidth() / nodeWidth; - } - g2.scale(scale, scale); - g2.translate(-nodeBounds.x, -nodeBounds.y); - break; - case FILL_STRATEGY_ASPECT_COVER: - // scale the graphics so node completely covers the imageable - // area, but retains its aspect ratio. - if (nodeRatio <= imageRatio) { - scale = image.getWidth() / nodeWidth; - } - else { - scale = image.getHeight() / nodeHeight; - } - g2.scale(scale, scale); - break; - case FILL_STRATEGY_EXACT_FIT: - // scale the node so that it covers then entire image, - // distorting it if necessary. - g2.scale(image.getWidth() / nodeWidth, image.getHeight() / nodeHeight); - g2.translate(-nodeBounds.x, -nodeBounds.y); - break; - default: - throw new IllegalArgumentException("Fill strategy provided is invalid"); - } - - final PPaintContext pc = new PPaintContext(g2); - pc.setRenderQuality(PPaintContext.HIGH_QUALITY_RENDERING); - fullPaint(pc); - return image; - } - - /** - * Constructs a new PrinterJob, allows the user to select which printer to - * print to, And then prints the node. - * @throws PrinterException if print fails - */ - public void print() throws PrinterException { - final PrinterJob printJob = PrinterJob.getPrinterJob(); - final PageFormat pageFormat = printJob.defaultPage(); - final Book book = new Book(); - book.append(this, pageFormat); - printJob.setPageable(book); - - if (printJob.printDialog()) { - printJob.print(); - } - } - - /** - * Prints the node into the given Graphics context using the specified - * format. The zero based index of the requested page is specified by - * pageIndex. If the requested page does not exist then this method returns - * NO_SUCH_PAGE; otherwise PAGE_EXISTS is returned. If the printable object - * aborts the print job then it throws a PrinterException. - * - * @param graphics the context into which the node is drawn - * @param pageFormat the size and orientation of the page - * @param pageIndex the zero based index of the page to be drawn - * - * @return Either NO_SUCH_PAGE or PAGE_EXISTS - */ - public int print(final Graphics graphics, final PageFormat pageFormat, final int pageIndex) { - if (pageIndex != 0) { - return NO_SUCH_PAGE; - } - - if (!(graphics instanceof Graphics2D)) { - throw new IllegalArgumentException("Provided graphics context is not a Graphics2D object"); - } - - final Graphics2D g2 = (Graphics2D) graphics; - final PBounds imageBounds = getFullBounds(); - - imageBounds.expandNearestIntegerDimensions(); - - g2.setClip(0, 0, (int) pageFormat.getWidth(), (int) pageFormat.getHeight()); - g2.translate(pageFormat.getImageableX(), pageFormat.getImageableY()); - - // scale the graphics so node's full bounds fit in the imageable bounds. - double scale = pageFormat.getImageableWidth() / imageBounds.getWidth(); - if (pageFormat.getImageableHeight() / imageBounds.getHeight() < scale) { - scale = pageFormat.getImageableHeight() / imageBounds.getHeight(); - } - - g2.scale(scale, scale); - g2.translate(-imageBounds.x, -imageBounds.y); - - final PPaintContext pc = new PPaintContext(g2); - pc.setRenderQuality(PPaintContext.HIGH_QUALITY_RENDERING); - fullPaint(pc); - - return PAGE_EXISTS; - } - - // **************************************************************** - // Picking - Methods for picking this node and its children. - // - // Picking is used to determine the node that intersects a point or - // rectangle on the screen. It is most frequently used by the - // PInputManager to determine the node that the cursor is over. - // - // The intersects() method is used to determine if a node has - // been picked or not. The default implementation just test to see - // if the pick bounds intersects the bounds of the node. Subclasses - // whose geometry (a circle for example) does not match up exactly with - // the bounds should override the intersects() method. - // - // The default picking behavior is to first try to pick the nodes - // children, and then try to pick the nodes own bounds. If a node - // wants specialized picking behavior it can override: - // - // pick() - Pick nodes here that should be picked before the nodes - // children are picked. - // pickAfterChildren() - Pick nodes here that should be picked after the - // node's children are picked. - // - // Note that fullPick should not normally be overridden. - // - // The pickable and childrenPickable flags can be used to make a - // node or it children not pickable even if their geometry does - // intersect the pick bounds. - // **************************************************************** - - /** - * Return true if this node is pickable. Only pickable nodes can receive - * input events. Nodes are pickable by default. - * - * @return true if this node is pickable - */ - public boolean getPickable() { - return pickable; - } - - /** - * Set the pickable flag for this node. Only pickable nodes can receive - * input events. Nodes are pickable by default. - * - * @param isPickable true if this node is pickable - */ - public void setPickable(final boolean isPickable) { - if (getPickable() != isPickable) { - pickable = isPickable; - firePropertyChange(PROPERTY_CODE_PICKABLE, PROPERTY_PICKABLE, null, null); - } - } - - /** - * Return true if the children of this node should be picked. If this flag - * is false then this node will not try to pick its children. Children are - * pickable by default. - * - * @return true if this node tries to pick its children - */ - public boolean getChildrenPickable() { - return childrenPickable; - } - - /** - * Set the children pickable flag. If this flag is false then this node will - * not try to pick its children. Children are pickable by default. - * - * @param areChildrenPickable true if this node tries to pick its children - */ - public void setChildrenPickable(final boolean areChildrenPickable) { - if (getChildrenPickable() != areChildrenPickable) { - childrenPickable = areChildrenPickable; - firePropertyChange(PROPERTY_CODE_CHILDREN_PICKABLE, PROPERTY_CHILDREN_PICKABLE, null, null); - } - } - - /** - * Try to pick this node before its children have had a chance to be picked. - * Nodes that paint on top of their children may want to override this - * method to if the pick path intersects that paint. - * - * @param pickPath the pick path used for the pick operation - * @return true if this node was picked - */ - protected boolean pick(final PPickPath pickPath) { - return false; - } - - /** - * Try to pick this node and all of its descendants. Most subclasses should - * not need to override this method. Instead they should override - * pick or pickAfterChildren. - * - * @param pickPath the pick path to add the node to if its picked - * @return true if this node or one of its descendants was picked. - */ - public boolean fullPick(final PPickPath pickPath) { - if (getVisible() && (getPickable() || getChildrenPickable()) && fullIntersects(pickPath.getPickBounds())) { - pickPath.pushNode(this); - pickPath.pushTransform(transform); - - final boolean thisPickable = getPickable() && pickPath.acceptsNode(this); - - if (thisPickable && pick(pickPath)) { - return true; - } - - if (getChildrenPickable()) { - final int count = getChildrenCount(); - for (int i = count - 1; i >= 0; i--) { - final PNode each = (PNode) children.get(i); - if (each.fullPick(pickPath)) { - return true; - } - } - } - - if (thisPickable && pickAfterChildren(pickPath)) { - return true; - } - - pickPath.popTransform(transform); - pickPath.popNode(this); - } - - return false; - } - - /** - * Finds all descendants of this node that intersect with the given bounds - * and adds them to the results array. - * - * @param fullBounds bounds to compare against - * @param results array into which to add matches - */ - public void findIntersectingNodes(final Rectangle2D fullBounds, final ArrayList results) { - if (fullIntersects(fullBounds)) { - final Rectangle2D localBounds = parentToLocal((Rectangle2D) fullBounds.clone()); - - if (intersects(localBounds)) { - results.add(this); - } - - final int count = getChildrenCount(); - for (int i = count - 1; i >= 0; i--) { - final PNode each = (PNode) children.get(i); - each.findIntersectingNodes(localBounds, results); - } - } - } - - /** - * Try to pick this node after its children have had a chance to be picked. - * Most subclasses the define a different geometry will need to override - * this method. - * - * @param pickPath the pick path used for the pick operation - * @return true if this node was picked - */ - protected boolean pickAfterChildren(final PPickPath pickPath) { - if (intersects(pickPath.getPickBounds())) { - return true; - } - return false; - } - - // **************************************************************** - // Structure - Methods for manipulating and traversing the - // parent child relationship - // - // Most of these methods won't need to be overridden by subclasses - // but you will use them frequently to build up your node structures. - // **************************************************************** - - /** - * Add a node to be a new child of this node. The new node is added to the - * end of the list of this node's children. If child was previously a child - * of another node, it is removed from that first. - * - * @param child the new child to add to this node - */ - public void addChild(final PNode child) { - int insertIndex = getChildrenCount(); - if (child.parent == this) { - insertIndex--; - } - addChild(insertIndex, child); - } - - /** - * Add a node to be a new child of this node at the specified index. If - * child was previously a child of another node, it is removed from that - * node first. - * - * @param index where in the children list to insert the child - * @param child the new child to add to this node - */ - public void addChild(final int index, final PNode child) { - final PNode oldParent = child.getParent(); - - if (oldParent != null) { - oldParent.removeChild(child); - } - - child.setParent(this); - getChildrenReference().add(index, child); - child.invalidatePaint(); - invalidateFullBounds(); - - firePropertyChange(PROPERTY_CODE_CHILDREN, PROPERTY_CHILDREN, null, children); - } - - /** - * Add a collection of nodes to be children of this node. If these nodes - * already have parents they will first be removed from those parents. - * - * @param nodes a collection of nodes to be added to this node - */ - public void addChildren(final Collection nodes) { - final Iterator i = nodes.iterator(); - while (i.hasNext()) { - final PNode each = (PNode) i.next(); - addChild(each); - } - } - - /** - * Return true if this node is an ancestor of the parameter node. - * - * @param node a possible descendant node - * @return true if this node is an ancestor of the given node - */ - public boolean isAncestorOf(final PNode node) { - PNode p = node.parent; - while (p != null) { - if (p == this) { - return true; - } - p = p.parent; - } - return false; - } - - /** - * Return true if this node is a descendant of the parameter node. - * - * @param node a possible ancestor node - * @return true if this nodes descends from the given node - */ - public boolean isDescendentOf(final PNode node) { - PNode p = parent; - while (p != null) { - if (p == node) { - return true; - } - p = p.parent; - } - return false; - } - - /** - * Return true if this node descends from the root. - * - * @return whether this node descends from root node - */ - public boolean isDescendentOfRoot() { - return getRoot() != null; - } - - /** - * Raise this node within the Z-order of its parent. - * - * @since 3.0 - */ - public void raise() { - final PNode p = parent; - if (p != null) { - final int index = parent.indexOfChild(this); - final int siblingIndex = Math.min(parent.getChildrenCount() - 1, index + 1); - if (siblingIndex != index) { - raiseAbove(parent.getChild(siblingIndex)); - } - } - } - - /** - * Lower this node within the Z-order of its parent. - * - * @since 3.0 - */ - public void lower() { - final PNode p = parent; - if (p != null) { - final int index = parent.indexOfChild(this); - final int siblingIndex = Math.max(0, index - 1); - if (siblingIndex != index) { - lowerBelow(parent.getChild(siblingIndex)); - } - } - } - - /** - * Raise this node within the Z-order of its parent to the top. - * - * @since 3.0 - */ - public void raiseToTop() { - final PNode p = parent; - if (p != null) { - p.removeChild(this); - p.addChild(this); - } - } - - /** - * Lower this node within the Z-order of its parent to the bottom. - * - * @since 3.0 - */ - public void lowerToBottom() { - final PNode p = parent; - if (p != null) { - p.removeChild(this); - p.addChild(0, this); - } - } - - /** - * Raise this node within the Z-order of its parent above the specified sibling node. - * - * @since 3.0 - * @param sibling sibling node to raise this node above - */ - public void raiseAbove(final PNode sibling) { - final PNode p = parent; - if (p != null && p == sibling.getParent()) { - p.removeChild(this); - final int index = p.indexOfChild(sibling); - p.addChild(index + 1, this); - } - } - - /** - * Lower this node within the Z-order of its parent below the specified sibling node. - * - * @since 3.0 - * @param sibling sibling node to lower this node below - */ - public void lowerBelow(final PNode sibling) { - final PNode p = parent; - if (p != null && p == sibling.getParent()) { - p.removeChild(this); - final int index = p.indexOfChild(sibling); - p.addChild(index, this); - } - } - - /** - * Raise the specified child node within the Z-order of this. - * - * @since 3.0 - * @param child child node to raise - */ - public void raise(final PNode child) { - if (children != null && children.contains(child) && this.equals(child.getParent())) { - child.raise(); - } - } - - /** - * Lower the specified child node within the Z-order of this. - * - * @since 3.0 - * @param child child node to lower - */ - public void lower(final PNode child) { - if (children != null && children.contains(child) && this.equals(child.getParent())) { - child.lower(); - } - } - - /** - * Raise the specified child node within the Z-order of this to the top. - * - * @since 3.0 - * @param child child node to raise to the top - */ - public void raiseToTop(final PNode child) { - if (children != null && children.contains(child) && this.equals(child.getParent())) { - child.raiseToTop(); - } - } - - /** - * Lower the specified child node within the Z-order of this to the bottom. - * - * @since 3.0 - * @param child child node to lower to the bottom - */ - public void lowerToBottom(final PNode child) { - if (children != null && children.contains(child) && this.equals(child.getParent())) { - child.lowerToBottom(); - } - } - - /** - * Return the parent of this node. This will be null if this node has not - * been added to a parent yet. - * - * @return this nodes parent or null - */ - public PNode getParent() { - return parent; - } - - /** - * Set the parent of this node. Note this is set automatically when adding - * and removing children. - * - * @param newParent the parent to which this node should be added - */ - public void setParent(final PNode newParent) { - final PNode old = parent; - parent = newParent; - firePropertyChange(PROPERTY_CODE_PARENT, PROPERTY_PARENT, old, parent); - } - - /** - * Return the index where the given child is stored. - * - * @param child child so search for - * @return index of child or -1 if not found - */ - public int indexOfChild(final PNode child) { - if (children == null) { - return -1; - } - return children.indexOf(child); - } - - /** - * Remove the given child from this node's children list. Any subsequent - * children are shifted to the left (one is subtracted from their indices). - * The removed child's parent is set to null. - * - * @param child the child to remove - * @return the removed child - */ - public PNode removeChild(final PNode child) { - final int index = indexOfChild(child); - if (index == -1) { - return null; - } - return removeChild(index); - } - - /** - * Remove the child at the specified position of this group node's children. - * Any subsequent children are shifted to the left (one is subtracted from - * their indices). The removed child's parent is set to null. - * - * @param index the index of the child to remove - * @return the removed child - */ - public PNode removeChild(final int index) { - if (children == null) { - return null; - } - final PNode child = (PNode) children.remove(index); - - if (children.size() == 0) { - children = null; - } - - child.repaint(); - child.setParent(null); - invalidateFullBounds(); - - firePropertyChange(PROPERTY_CODE_CHILDREN, PROPERTY_CHILDREN, null, children); - - return child; - } - - /** - * Remove all the children in the given collection from this node's list of - * children. All removed nodes will have their parent set to null. - * - * @param childrenNodes the collection of children to remove - */ - public void removeChildren(final Collection childrenNodes) { - final Iterator i = childrenNodes.iterator(); - while (i.hasNext()) { - final PNode each = (PNode) i.next(); - removeChild(each); - } - } - - /** - * Remove all the children from this node. Node this method is more - * efficient then removing each child individually. - */ - public void removeAllChildren() { - if (children != null) { - final int count = children.size(); - for (int i = 0; i < count; i++) { - final PNode each = (PNode) children.get(i); - each.setParent(null); - } - children = null; - invalidatePaint(); - invalidateFullBounds(); - - firePropertyChange(PROPERTY_CODE_CHILDREN, PROPERTY_CHILDREN, null, children); - } - } - - /** - * Delete this node by removing it from its parent's list of children. - */ - public void removeFromParent() { - if (parent != null) { - parent.removeChild(this); - } - } - - /** - * Set the parent of this node, and transform the node in such a way that it - * doesn't move in global coordinates. - * - * @param newParent The new parent of this node. - */ - public void reparent(final PNode newParent) { - final AffineTransform originalTransform = getLocalToGlobalTransform(null); - final AffineTransform newTransform = newParent.getGlobalToLocalTransform(null); - newTransform.concatenate(originalTransform); - - removeFromParent(); - setTransform(newTransform); - newParent.addChild(this); - computeFullBounds(fullBoundsCache); - } - - /** - * Swaps this node out of the scene graph tree, and replaces it with the - * specified replacement node. This node is left dangling, and it is up to - * the caller to manage it. The replacement node will be added to this - * node's parent in the same position as this was. That is, if this was the - * 3rd child of its parent, then after calling replaceWith(), the - * replacement node will also be the 3rd child of its parent. If this node - * has no parent when replace is called, then nothing will be done at all. - * - * @param replacementNode the new node that replaces the current node in the - * scene graph tree. - */ - public void replaceWith(final PNode replacementNode) { - if (parent != null) { - final PNode p = parent; - final int index = p.getChildrenReference().indexOf(this); - p.removeChild(this); - p.addChild(index, replacementNode); - } - } - - /** - * Sets the name of this node, may be null. - * - * @since 1.3 - * @param name new name for this node - */ - public void setName(final String name) { - this.name = name; - } - - /** - * Returns the name given to this node. - * - * @since 1.3 - * @return name given to this node, may be null - */ - public String getName() { - return name; - } - - /** - * Return the number of children that this node has. - * - * @return the number of children - */ - public int getChildrenCount() { - if (children == null) { - return 0; - } - return children.size(); - } - - /** - * Return the child node at the specified index. - * - * @param index a child index - * @return the child node at the specified index - */ - public PNode getChild(final int index) { - return (PNode) children.get(index); - } - - /** - * Return a reference to the list used to manage this node's children. This - * list should not be modified. - * - * @return reference to the children list - */ - public List getChildrenReference() { - if (children == null) { - children = new ArrayList(); - } - return children; - } - - /** - * Return an iterator over this node's direct descendant children. - * - * @return iterator over this nodes children - */ - public ListIterator getChildrenIterator() { - if (children == null) { - return Collections.EMPTY_LIST.listIterator(); - } - return Collections.unmodifiableList(children).listIterator(); - } - - /** - * Return the root node (instance of PRoot). If this node does not descend - * from a PRoot then null will be returned. - * - * @return root element of this node, or null if this node does not descend - * from a PRoot - */ - public PRoot getRoot() { - if (parent != null) { - return parent.getRoot(); - } - return null; - } - - /** - * Return a collection containing this node and all of its descendant nodes. - * - * @return a new collection containing this node and all descendants - */ - public Collection getAllNodes() { - return getAllNodes(null, null); - } - - /** - * Return a collection containing the subset of this node and all of its - * descendant nodes that are accepted by the given node filter. If the - * filter is null then all nodes will be accepted. If the results parameter - * is not null then it will be used to collect this subset instead of - * creating a new collection. - * - * @param filter the filter used to determine the subset - * @param resultantNodes where matching nodes should be added - * @return a collection containing this node and all descendants - */ - public Collection getAllNodes(final PNodeFilter filter, final Collection resultantNodes) { - Collection results; - if (resultantNodes == null) { - results = new ArrayList(); - } - else { - results = resultantNodes; - } - - if (filter == null || filter.accept(this)) { - results.add(this); - } - - if (filter == null || filter.acceptChildrenOf(this)) { - final int count = getChildrenCount(); - for (int i = 0; i < count; i++) { - final PNode each = (PNode) children.get(i); - each.getAllNodes(filter, results); - } - } - - return results; - } - - // **************************************************************** - // Serialization - Nodes conditionally serialize their parent. - // This means that only the parents that were unconditionally - // (using writeObject) serialized by someone else will be restored - // when the node is unserialized. - // **************************************************************** - - /** - * Write this node and all of its descendant nodes to the given outputsteam. - * This stream must be an instance of PObjectOutputStream or serialization - * will fail. This nodes parent is written out conditionally, that is it - * will only be written out if someone else writes it out unconditionally. - * - * @param out the output stream to write to, must be an instance of - * PObjectOutputStream - * @throws IOException when an error occurs speaking to underlying - * ObjectOutputStream - */ - private void writeObject(final ObjectOutputStream out) throws IOException { - if (!(out instanceof PObjectOutputStream)) { - throw new IllegalArgumentException("PNode.writeObject may only be used with PObjectOutputStreams"); - } - out.defaultWriteObject(); - ((PObjectOutputStream) out).writeConditionalObject(parent); - } - - /** - * Read this node and all of its descendants in from the given input stream. - * - * @param in the stream to read from - * - * @throws IOException when an error occurs speaking to underlying - * ObjectOutputStream - * @throws ClassNotFoundException when a class is deserialized that no - * longer exists. This can happen if it's renamed or deleted. - */ - private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { - in.defaultReadObject(); - parent = (PNode) in.readObject(); - } - - /** - * Returns an array of input event listeners that are attached to this node. - * - * @since 1.3 - * @return event listeners attached to this node - */ - public PInputEventListener[] getInputEventListeners() { - if (listenerList == null || listenerList.getListenerCount() == 0) { - return new PInputEventListener[] {}; - } - - final EventListener[] listeners = listenerList.getListeners(PInputEventListener.class); - - final PInputEventListener[] result = new PInputEventListener[listeners.length]; - for (int i = 0; i < listeners.length; i++) { - result[i] = (PInputEventListener) listeners[i]; - } - return result; - } - - /** - * PSceneGraphDelegate is an interface to receive low level node - * events. It together with PNode.SCENE_GRAPH_DELEGATE gives Piccolo2d users - * an efficient way to learn about low level changes in Piccolo's scene - * graph. Most users will not need to use this. - */ - public interface PSceneGraphDelegate { - /** - * Called to notify delegate that the node needs repainting. - * - * @param node node needing repaint - */ - void nodePaintInvalidated(PNode node); - - /** - * Called to notify delegate that the node and all it's children need - * repainting. - * - * @param node node needing repaint - */ - void nodeFullBoundsInvalidated(PNode node); - } + /** + * The minimum difference in transparency required before the transparency is + * allowed to change. Done for efficiency reasons. I doubt very much that the + * human eye could tell the difference between 0.01 and 0.02 transparency. + */ + private static final float TRANSPARENCY_RESOLUTION = 0.01f; + + /** + * Allows for future serialization code to understand versioned binary formats. + */ + private static final long serialVersionUID = 1L; + + /** + * The property name that identifies a change in this node's client properties + * (see {@link #getClientProperties getClientProperties}). In an property change + * event the new value will be a reference to the map of client properties but + * old value will always be null. + */ + public static final String PROPERTY_CLIENT_PROPERTIES = "clientProperties"; + + /** + * The property code that identifies a change in this node's client properties + * (see {@link #getClientProperties getClientProperties}). In an property change + * event the new value will be a reference to the map of client properties but + * old value will always be null. + */ + public static final int PROPERTY_CODE_CLIENT_PROPERTIES = 1 << 0; + + /** + * The property name that identifies a change of this node's bounds (see + * {@link #getBounds getBounds}, {@link #getBoundsReference + * getBoundsReference}). In any property change event the new value will be a + * reference to this node's bounds, but old value will always be null. + */ + public static final String PROPERTY_BOUNDS = "bounds"; + + /** + * The property code that identifies a change of this node's bounds (see + * {@link #getBounds getBounds}, {@link #getBoundsReference + * getBoundsReference}). In any property change event the new value will be a + * reference to this node's bounds, but old value will always be null. + */ + public static final int PROPERTY_CODE_BOUNDS = 1 << 1; + + /** + * The property name that identifies a change of this node's full bounds (see + * {@link #getFullBounds getFullBounds}, {@link #getFullBoundsReference + * getFullBoundsReference}). In any property change event the new value will be + * a reference to this node's full bounds cache, but old value will always be + * null. + */ + public static final String PROPERTY_FULL_BOUNDS = "fullBounds"; + + /** + * The property code that identifies a change of this node's full bounds (see + * {@link #getFullBounds getFullBounds}, {@link #getFullBoundsReference + * getFullBoundsReference}). In any property change event the new value will be + * a reference to this node's full bounds cache, but old value will always be + * null. + */ + public static final int PROPERTY_CODE_FULL_BOUNDS = 1 << 2; + + /** + * The property name that identifies a change of this node's transform (see + * {@link #getTransform getTransform}, {@link #getTransformReference + * getTransformReference}). In any property change event the new value will be a + * reference to this node's transform, but old value will always be null. + */ + public static final String PROPERTY_TRANSFORM = "transform"; + + /** + * The property code that identifies a change of this node's transform (see + * {@link #getTransform getTransform}, {@link #getTransformReference + * getTransformReference}). In any property change event the new value will be a + * reference to this node's transform, but old value will always be null. + */ + public static final int PROPERTY_CODE_TRANSFORM = 1 << 3; + + /** + * The property name that identifies a change of this node's visibility (see + * {@link #getVisible getVisible}). Both old value and new value will be null in + * any property change event. + */ + public static final String PROPERTY_VISIBLE = "visible"; + + /** + * The property code that identifies a change of this node's visibility (see + * {@link #getVisible getVisible}). Both old value and new value will be null in + * any property change event. + */ + public static final int PROPERTY_CODE_VISIBLE = 1 << 4; + + /** + * The property name that identifies a change of this node's paint (see + * {@link #getPaint getPaint}). Both old value and new value will be set + * correctly in any property change event. + */ + public static final String PROPERTY_PAINT = "paint"; + + /** + * The property code that identifies a change of this node's paint (see + * {@link #getPaint getPaint}). Both old value and new value will be set + * correctly in any property change event. + */ + public static final int PROPERTY_CODE_PAINT = 1 << 5; + + /** + * The property name that identifies a change of this node's transparency (see + * {@link #getTransparency getTransparency}). Both old value and new value will + * be null in any property change event. + */ + public static final String PROPERTY_TRANSPARENCY = "transparency"; + + /** + * The property code that identifies a change of this node's transparency (see + * {@link #getTransparency getTransparency}). Both old value and new value will + * be null in any property change event. + */ + public static final int PROPERTY_CODE_TRANSPARENCY = 1 << 6; + + /** + * The property name that identifies a change of this node's pickable status + * (see {@link #getPickable getPickable}). Both old value and new value will be + * null in any property change event. + */ + public static final String PROPERTY_PICKABLE = "pickable"; + /** + * The property code that identifies a change of this node's pickable status + * (see {@link #getPickable getPickable}). Both old value and new value will be + * null in any property change event. + */ + public static final int PROPERTY_CODE_PICKABLE = 1 << 7; + + /** + * The property name that identifies a change of this node's children pickable + * status (see {@link #getChildrenPickable getChildrenPickable}). Both old value + * and new value will be null in any property change event. + */ + public static final String PROPERTY_CHILDREN_PICKABLE = "childrenPickable"; + + /** + * The property code that identifies a change of this node's children pickable + * status (see {@link #getChildrenPickable getChildrenPickable}). Both old value + * and new value will be null in any property change event. + */ + public static final int PROPERTY_CODE_CHILDREN_PICKABLE = 1 << 8; + + /** + * The property name that identifies a change in the set of this node's direct + * children (see {@link #getChildrenReference getChildrenReference}, + * {@link #getChildrenIterator getChildrenIterator}). In any property change + * event the new value will be a reference to this node's children, but old + * value will always be null. + */ + public static final String PROPERTY_CHILDREN = "children"; + + /** + * The property code that identifies a change in the set of this node's direct + * children (see {@link #getChildrenReference getChildrenReference}, + * {@link #getChildrenIterator getChildrenIterator}). In any property change + * event the new value will be a reference to this node's children, but old + * value will always be null. + */ + public static final int PROPERTY_CODE_CHILDREN = 1 << 9; + + /** + * The property name that identifies a change of this node's parent (see + * {@link #getParent getParent}). Both old value and new value will be set + * correctly in any property change event. + */ + public static final String PROPERTY_PARENT = "parent"; + + /** + * The property code that identifies a change of this node's parent (see + * {@link #getParent getParent}). Both old value and new value will be set + * correctly in any property change event. + */ + public static final int PROPERTY_CODE_PARENT = 1 << 10; + + /** Is an optimization for use during repaints. */ + private static final PBounds TEMP_REPAINT_BOUNDS = new PBounds(); + + /** The single scene graph delegate that receives low level node events. */ + public static PSceneGraphDelegate SCENE_GRAPH_DELEGATE = null; + + /** Tracks the parent of this node, may be null. */ + private transient PNode parent; + + /** Tracks all immediate child nodes. */ + private List children; + + /** Bounds of the PNode. */ + private final PBounds bounds; + + /** Transform that applies to this node in relation to its parent. */ + private PAffineTransform transform; + + /** The paint to use for the background of this node. */ + private Paint paint; + + /** + * How Opaque this node should be 1f = fully opaque, 0f = completely + * transparent. + */ + private float transparency; + + /** A modifiable set of client properties. */ + private MutableAttributeSet clientProperties; + + /** + * An optimization that remembers the full bounds of a node rather than + * computing it every time. + */ + private PBounds fullBoundsCache; + + /** + * Mask used when deciding whether to bubble up property change events to + * parents. + */ + private int propertyChangeParentMask = 0; + + /** Used to handle property change listeners. */ + private transient SwingPropertyChangeSupport changeSupport; + + /** List of event listeners. */ + private transient EventListenerList listenerList; + + /** Whether this node is pickable or not. */ + private boolean pickable; + + /** + * Whether to stop processing pick at this node and not bother drilling down + * into children. + */ + private boolean childrenPickable; + + /** Whether this node will be rendered. */ + private boolean visible; + + private boolean childBoundsVolatile; + + /** Whether this node needs to be repainted. */ + private boolean paintInvalid; + + /** Whether children need to be repainted. */ + private boolean childPaintInvalid; + + /** Whether this node's bounds have changed, and so needs to be relaid out. */ + private boolean boundsChanged; + + /** Whether this node's full bounds need to be recomputed. */ + private boolean fullBoundsInvalid; + + /** Whether this node's child bounds need to be recomputed. */ + private boolean childBoundsInvalid; + + private boolean occluded; + + /** Stores the name associated to this node. */ + private String name; + + /** + * toImage fill strategy that stretches the node be as large as possible while + * still retaining its aspect ratio. + * + * @since 1.3 + */ + public static final int FILL_STRATEGY_ASPECT_FIT = 1; + + /** + * toImage fill strategy that stretches the node be large enough to cover the + * image, and centers it. + * + * @since 1.3 + */ + public static final int FILL_STRATEGY_ASPECT_COVER = 2; + + /** + * toImage fill strategy that stretches the node to be exactly the dimensions of + * the image. Will result in distortion if the aspect ratios are different. + * + * @since 1.3 + */ + public static final int FILL_STRATEGY_EXACT_FIT = 4; + + /** + * Creates a new PNode with the given name. + * + * @since 1.3 + * @param newName name to assign to node + */ + public PNode(final String newName) { + this(); + setName(newName); + } + + /** + * Constructs a new PNode. + *

+ * By default a node's paint is null, and bounds are empty. These values must be + * set for the node to show up on the screen once it's added to a scene graph. + */ + public PNode() { + bounds = new PBounds(); + fullBoundsCache = new PBounds(); + transparency = 1.0f; + pickable = true; + childrenPickable = true; + visible = true; + } + + // **************************************************************** + // Animation - Methods to animate this node. + // + // Note that animation is implemented by activities (PActivity), + // so if you need more control over your animation look at the + // activities package. Each animate method creates an animation that + // will animate the node from its current state to the new state + // specified over the given duration. These methods will try to + // automatically schedule the new activity, but if the node does not + // descend from the root node when the method is called then the + // activity will not be scheduled and you must schedule it manually. + // **************************************************************** + + /** + * Animate this node's bounds from their current location when the activity + * starts to the specified bounds. If this node descends from the root then the + * activity will be scheduled, else the returned activity should be scheduled + * manually. If two different transform activities are scheduled for the same + * node at the same time, they will both be applied to the node, but the last + * one scheduled will be applied last on each frame, so it will appear to have + * replaced the original. Generally you will not want to do that. Note this + * method animates the node's bounds, but does not change the node's transform. + * Use animateTransformToBounds() to animate the node's transform instead. + * + * @param x left of target bounds + * @param y top of target bounds + * @param width width of target bounds + * @param height height of target bounds + * @param duration amount of time that the animation should take + * @return the newly scheduled activity + */ + public PInterpolatingActivity animateToBounds(final double x, final double y, final double width, + final double height, final long duration) { + if (duration == 0) { + setBounds(x, y, width, height); + return null; + } + + final PBounds dst = new PBounds(x, y, width, height); + + final PInterpolatingActivity interpolatingActivity = new PInterpolatingActivity(duration, + PUtil.DEFAULT_ACTIVITY_STEP_RATE) { + private PBounds src; + + protected void activityStarted() { + src = getBounds(); + startResizeBounds(); + super.activityStarted(); + } + + public void setRelativeTargetValue(final float zeroToOne) { + PNode.this.setBounds(src.x + zeroToOne * (dst.x - src.x), src.y + zeroToOne * (dst.y - src.y), + src.width + zeroToOne * (dst.width - src.width), + src.height + zeroToOne * (dst.height - src.height)); + } + + protected void activityFinished() { + super.activityFinished(); + endResizeBounds(); + } + }; + + addActivity(interpolatingActivity); + return interpolatingActivity; + } + + /** + * Animate this node from it's current transform when the activity starts a new + * transform that will fit the node into the given bounds. If this node descends + * from the root then the activity will be scheduled, else the returned activity + * should be scheduled manually. If two different transform activities are + * scheduled for the same node at the same time, they will both be applied to + * the node, but the last one scheduled will be applied last on each frame, so + * it will appear to have replaced the original. Generally you will not want to + * do that. Note this method animates the node's transform, but does not + * directly change the node's bounds rectangle. Use animateToBounds() to animate + * the node's bounds rectangle instead. + * + * @param x left of target bounds + * @param y top of target bounds + * @param width width of target bounds + * @param height height of target bounds + * @param duration amount of time that the animation should take + * @return the newly scheduled activity + */ + public PTransformActivity animateTransformToBounds(final double x, final double y, final double width, + final double height, final long duration) { + final PAffineTransform t = new PAffineTransform(); + t.setToScale(width / getWidth(), height / getHeight()); + final double scale = t.getScale(); + t.setOffset(x - getX() * scale, y - getY() * scale); + return animateToTransform(t, duration); + } + + /** + * Animate this node's transform from its current location when the activity + * starts to the specified location, scale, and rotation. If this node descends + * from the root then the activity will be scheduled, else the returned activity + * should be scheduled manually. If two different transform activities are + * scheduled for the same node at the same time, they will both be applied to + * the node, but the last one scheduled will be applied last on each frame, so + * it will appear to have replaced the original. Generally you will not want to + * do that. + * + * @param x the final target x position of node + * @param y the final target y position of node + * @param duration amount of time that the animation should take + * @param scale the final scale for the duration + * @param theta final theta value (in radians) for the animation + * @return the newly scheduled activity + */ + public PTransformActivity animateToPositionScaleRotation(final double x, final double y, final double scale, + final double theta, final long duration) { + final PAffineTransform t = getTransform(); + t.setOffset(x, y); + t.setScale(scale); + t.setRotation(theta); + return animateToTransform(t, duration); + } + + /** + * Animate this node's transform from its current values when the activity + * starts to the new values specified in the given transform. If this node + * descends from the root then the activity will be scheduled, else the returned + * activity should be scheduled manually. If two different transform activities + * are scheduled for the same node at the same time, they will both be applied + * to the node, but the last one scheduled will be applied last on each frame, + * so it will appear to have replaced the original. Generally you will not want + * to do that. + * + * @param destTransform the final transform value + * @param duration amount of time that the animation should take + * @return the newly scheduled activity + */ + public PTransformActivity animateToTransform(final AffineTransform destTransform, final long duration) { + if (duration == 0) { + setTransform(destTransform); + return null; + } else { + final PTransformActivity.Target t = new PTransformActivity.Target() { + public void setTransform(final AffineTransform aTransform) { + PNode.this.setTransform(aTransform); + } + + public void getSourceMatrix(final double[] aSource) { + PNode.this.getTransformReference(true).getMatrix(aSource); + } + }; + + final PTransformActivity ta = new PTransformActivity(duration, PUtil.DEFAULT_ACTIVITY_STEP_RATE, t, + destTransform); + addActivity(ta); + return ta; + } + } + + /** + * Animate this node's color from its current value to the new value specified. + * This meathod assumes that this nodes paint property is of type color. If this + * node descends from the root then the activity will be scheduled, else the + * returned activity should be scheduled manually. If two different color + * activities are scheduled for the same node at the same time, they will both + * be applied to the node, but the last one scheduled will be applied last on + * each frame, so it will appear to have replaced the original. Generally you + * will not want to do that. + * + * @param destColor final color value. + * @param duration amount of time that the animation should take + * @return the newly scheduled activity + */ + public PInterpolatingActivity animateToColor(final Color destColor, final long duration) { + if (duration == 0) { + setPaint(destColor); + return null; + } else { + final PColorActivity.Target t = new PColorActivity.Target() { + public Color getColor() { + return (Color) getPaint(); + } + + public void setColor(final Color color) { + setPaint(color); + } + }; + + final PColorActivity ca = new PColorActivity(duration, PUtil.DEFAULT_ACTIVITY_STEP_RATE, t, destColor); + addActivity(ca); + return ca; + } + } + + /** + * Animate this node's transparency from its current value to the new value + * specified. Transparency values must range from zero to one. If this node + * descends from the root then the activity will be scheduled, else the returned + * activity should be scheduled manually. If two different transparency + * activities are scheduled for the same node at the same time, they will both + * be applied to the node, but the last one scheduled will be applied last on + * each frame, so it will appear to have replaced the original. Generally you + * will not want to do that. + * + * @param zeroToOne final transparency value. + * @param duration amount of time that the animation should take + * @return the newly scheduled activity + */ + public PInterpolatingActivity animateToTransparency(final float zeroToOne, final long duration) { + if (duration == 0) { + setTransparency(zeroToOne); + return null; + } else { + final float dest = zeroToOne; + + final PInterpolatingActivity ta = new PInterpolatingActivity(duration, PUtil.DEFAULT_ACTIVITY_STEP_RATE) { + private float source; + + protected void activityStarted() { + source = getTransparency(); + super.activityStarted(); + } + + public void setRelativeTargetValue(final float zeroToOne) { + PNode.this.setTransparency(source + zeroToOne * (dest - source)); + } + }; + + addActivity(ta); + return ta; + } + } + + /** + * Schedule the given activity with the root, note that only scheduled + * activities will be stepped. If the activity is successfully added true is + * returned, else false. + * + * @param activity new activity to schedule + * @return true if the activity is successfully scheduled. + */ + public boolean addActivity(final PActivity activity) { + final PRoot r = getRoot(); + if (r != null) { + return r.addActivity(activity); + } + return false; + } + + // **************************************************************** + // Client Properties - Methods for managing client properties for + // this node. + // + // Client properties provide a way for programmers to attach + // extra information to a node without having to subclass it and + // add new instance variables. + // **************************************************************** + + /** + * Return mutable attributed set of client properties associated with this node. + * + * @return the client properties associated to this node + */ + public MutableAttributeSet getClientProperties() { + if (clientProperties == null) { + clientProperties = new SimpleAttributeSet(); + } + return clientProperties; + } + + /** + * Returns the value of the client attribute with the specified key. Only + * attributes added with addAttribute will return a non-null value. + * + * @param key key to use while fetching client attribute + * + * @return the value of this attribute or null + */ + public Object getAttribute(final Object key) { + if (clientProperties == null || key == null) { + return null; + } else { + return clientProperties.getAttribute(key); + } + } + + /** + * Add an arbitrary key/value to this node. + *

+ * The get/add attribute methods provide access to a small + * per-instance attribute set. Callers can use get/add attribute to annotate + * nodes that were created by another module. + *

+ * If value is null this method will remove the attribute. + * + * @param key to use when adding the attribute + * @param value value to associate to the new attribute + */ + public void addAttribute(final Object key, final Object value) { + if (value == null && clientProperties == null) { + return; + } + + final Object oldValue = getAttribute(key); + + if (value != oldValue) { + if (clientProperties == null) { + clientProperties = new SimpleAttributeSet(); + } + + if (value == null) { + clientProperties.removeAttribute(key); + } else { + clientProperties.addAttribute(key, value); + } + + if (clientProperties.getAttributeCount() == 0 && clientProperties.getResolveParent() == null) { + clientProperties = null; + } + + firePropertyChange(PROPERTY_CODE_CLIENT_PROPERTIES, PROPERTY_CLIENT_PROPERTIES, null, clientProperties); + firePropertyChange(PROPERTY_CODE_CLIENT_PROPERTIES, key.toString(), oldValue, value); + } + } + + /** + * Returns an enumeration of all keys maped to attribute values values. + * + * @return an Enumeration over attribute keys + */ + public Enumeration getClientPropertyKeysEnumeration() { + if (clientProperties == null) { + return PUtil.NULL_ENUMERATION; + } else { + return clientProperties.getAttributeNames(); + } + } + + // convenience methods for attributes + + /** + * Fetches the value of the requested attribute, returning defaultValue is not + * found. + * + * @param key attribute to search for + * @param defaultValue value to return if attribute is not found + * + * @return value of attribute or defaultValue if not found + */ + public Object getAttribute(final Object key, final Object defaultValue) { + final Object value = getAttribute(key); + if (value == null) { + return defaultValue; + } + + return value; + } + + /** + * Fetches the boolean value of the requested attribute, returning defaultValue + * is not found. + * + * @param key attribute to search for + * @param defaultValue value to return if attribute is not found + * + * @return value of attribute or defaultValue if not found + */ + public boolean getBooleanAttribute(final Object key, final boolean defaultValue) { + final Boolean value = (Boolean) getAttribute(key); + if (value == null) { + return defaultValue; + } + + return value.booleanValue(); + } + + /** + * Fetches the integer value of the requested attribute, returning defaultValue + * is not found. + * + * @param key attribute to search for + * @param defaultValue value to return if attribute is not found + * + * @return value of attribute or defaultValue if not found + */ + public int getIntegerAttribute(final Object key, final int defaultValue) { + final Number value = (Number) getAttribute(key); + if (value == null) { + return defaultValue; + } + + return value.intValue(); + } + + /** + * Fetches the double value of the requested attribute, returning defaultValue + * is not found. + * + * @param key attribute to search for + * @param defaultValue value to return if attribute is not found + * + * @return value of attribute or defaultValue if not found + */ + public double getDoubleAttribute(final Object key, final double defaultValue) { + final Number value = (Number) getAttribute(key); + if (value == null) { + return defaultValue; + } + + return value.doubleValue(); + } + + // **************************************************************** + // Copying - Methods for copying this node and its descendants. + // Copying is implemented in terms of serialization. + // **************************************************************** + + /** + * The copy method copies this node and all of its descendants. Note that + * copying is implemented in terms of java serialization. See the serialization + * notes for more information. + * + * @return new copy of this node or null if the node was not serializable + */ + public Object clone() { + try { + final byte[] ser = PObjectOutputStream.toByteArray(this); + return new ObjectInputStream(new ByteArrayInputStream(ser)).readObject(); + } catch (final IOException e) { + return null; + } catch (final ClassNotFoundException e) { + return null; + } + } + + // **************************************************************** + // Coordinate System Conversions - Methods for converting + // geometry between this nodes local coordinates and the other + // major coordinate systems. + // + // Each nodes has an affine transform that it uses to define its + // own coordinate system. For example if you create a new node and + // add it to the canvas it will appear in the upper right corner. Its + // coordinate system matches the coordinate system of its parent + // (the root node) at this point. But if you move this node by calling + // node.translate() the nodes affine transform will be modified and the + // node will appear at a different location on the screen. The node + // coordinate system no longer matches the coordinate system of its + // parent. + // + // This is useful because it means that the node's methods for + // rendering and picking don't need to worry about the fact that + // the node has been moved to another position on the screen, they + // keep working just like they did when it was in the upper right + // hand corner of the screen. + // + // The problem is now that each node defines its own coordinate + // system it is difficult to compare the positions of two node with + // each other. These methods are all meant to help solve that problem. + // + // The terms used in the methods are as follows: + // + // local - The local or base coordinate system of a node. + // parent - The coordinate system of a node's parent + // global - The topmost coordinate system, above the root node. + // + // Normally when comparing the positions of two nodes you will + // convert the local position of each node to the global coordinate + // system, and then compare the positions in that common coordinate + // system. + // *************************************************************** + + /** + * Transform the given point from this node's local coordinate system to its + * parent's local coordinate system. Note that this will modify the point + * parameter. + * + * @param localPoint point in local coordinate system to be transformed. + * @return point in parent's local coordinate system + */ + public Point2D localToParent(final Point2D localPoint) { + if (transform == null) { + return localPoint; + } + return transform.transform(localPoint, localPoint); + } + + /** + * Transform the given dimension from this node's local coordinate system to its + * parent's local coordinate system. Note that this will modify the dimension + * parameter. + * + * @param localDimension dimension in local coordinate system to be transformed. + * @return dimension in parent's local coordinate system + */ + public Dimension2D localToParent(final Dimension2D localDimension) { + if (transform == null) { + return localDimension; + } + return transform.transform(localDimension, localDimension); + } + + /** + * Transform the given rectangle from this node's local coordinate system to its + * parent's local coordinate system. Note that this will modify the rectangle + * parameter. + * + * @param localRectangle rectangle in local coordinate system to be transformed. + * @return rectangle in parent's local coordinate system + */ + public Rectangle2D localToParent(final Rectangle2D localRectangle) { + if (transform == null) { + return localRectangle; + } + return transform.transform(localRectangle, localRectangle); + } + + /** + * Transform the given point from this node's parent's local coordinate system + * to the local coordinate system of this node. Note that this will modify the + * point parameter. + * + * @param parentPoint point in parent's coordinate system to be transformed. + * @return point in this node's local coordinate system + */ + public Point2D parentToLocal(final Point2D parentPoint) { + if (transform == null) { + return parentPoint; + } + + return transform.inverseTransform(parentPoint, parentPoint); + } + + /** + * Transform the given dimension from this node's parent's local coordinate + * system to the local coordinate system of this node. Note that this will + * modify the dimension parameter. + * + * @param parentDimension dimension in parent's coordinate system to be + * transformed. + * @return dimension in this node's local coordinate system + */ + public Dimension2D parentToLocal(final Dimension2D parentDimension) { + if (transform == null) { + return parentDimension; + } + return transform.inverseTransform(parentDimension, parentDimension); + } + + /** + * Transform the given rectangle from this node's parent's local coordinate + * system to the local coordinate system of this node. Note that this will + * modify the rectangle parameter. + * + * @param parentRectangle rectangle in parent's coordinate system to be + * transformed. + * @return rectangle in this node's local coordinate system + */ + public Rectangle2D parentToLocal(final Rectangle2D parentRectangle) { + if (transform == null) { + return parentRectangle; + } + return transform.inverseTransform(parentRectangle, parentRectangle); + } + + /** + * Transform the given point from this node's local coordinate system to the + * global coordinate system. Note that this will modify the point parameter. + * + * @param localPoint point in local coordinate system to be transformed. + * @return point in global coordinates + */ + public Point2D localToGlobal(final Point2D localPoint) { + PNode n = this; + while (n != null) { + n.localToParent(localPoint); + n = n.parent; + } + return localPoint; + } + + /** + * Transform the given dimension from this node's local coordinate system to the + * global coordinate system. Note that this will modify the dimension parameter. + * + * @param localDimension dimension in local coordinate system to be transformed. + * @return dimension in global coordinates + */ + public Dimension2D localToGlobal(final Dimension2D localDimension) { + PNode n = this; + while (n != null) { + n.localToParent(localDimension); + n = n.parent; + } + return localDimension; + } + + /** + * Transform the given rectangle from this node's local coordinate system to the + * global coordinate system. Note that this will modify the rectangle parameter. + * + * @param localRectangle rectangle in local coordinate system to be transformed. + * @return rectangle in global coordinates + */ + public Rectangle2D localToGlobal(final Rectangle2D localRectangle) { + PNode n = this; + while (n != null) { + n.localToParent(localRectangle); + n = n.parent; + } + return localRectangle; + } + + /** + * Transform the given point from global coordinates to this node's local + * coordinate system. Note that this will modify the point parameter. + * + * @param globalPoint point in global coordinates to be transformed. + * @return point in this node's local coordinate system. + */ + public Point2D globalToLocal(final Point2D globalPoint) { + final PAffineTransform globalTransform = computeGlobalTransform(this); + return globalTransform.inverseTransform(globalPoint, globalPoint); + } + + private PAffineTransform computeGlobalTransform(final PNode node) { + if (node == null) { + return new PAffineTransform(); + } + + final PAffineTransform parentGlobalTransform = computeGlobalTransform(node.parent); + if (node.transform != null) { + parentGlobalTransform.concatenate(node.transform); + } + return parentGlobalTransform; + } + + /** + * Transform the given dimension from global coordinates to this node's local + * coordinate system. Note that this will modify the dimension parameter. + * + * @param globalDimension dimension in global coordinates to be transformed. + * @return dimension in this node's local coordinate system. + */ + public Dimension2D globalToLocal(final Dimension2D globalDimension) { + if (parent != null) { + parent.globalToLocal(globalDimension); + } + return parentToLocal(globalDimension); + } + + /** + * Transform the given rectangle from global coordinates to this node's local + * coordinate system. Note that this will modify the rectangle parameter. + * + * @param globalRectangle rectangle in global coordinates to be transformed. + * @return rectangle in this node's local coordinate system. + */ + public Rectangle2D globalToLocal(final Rectangle2D globalRectangle) { + if (parent != null) { + parent.globalToLocal(globalRectangle); + } + return parentToLocal(globalRectangle); + } + + /** + * Return the transform that converts local coordinates at this node to the + * global coordinate system. + * + * @param dest PAffineTransform to transform to global coordinates + * @return The concatenation of transforms from the top node down to this node. + */ + public PAffineTransform getLocalToGlobalTransform(final PAffineTransform dest) { + PAffineTransform result = dest; + if (parent != null) { + result = parent.getLocalToGlobalTransform(result); + if (transform != null) { + result.concatenate(transform); + } + } else if (dest == null) { + result = getTransform(); + } else if (transform != null) { + result.setTransform(transform); + } else { + result.setToIdentity(); + } + + return result; + } + + /** + * Return the transform that converts global coordinates to local coordinates of + * this node. + * + * @param dest PAffineTransform to transform from global to local + * + * @return The inverse of the concatenation of transforms from the root down to + * this node. + */ + public PAffineTransform getGlobalToLocalTransform(final PAffineTransform dest) { + PAffineTransform result = getLocalToGlobalTransform(dest); + try { + result.setTransform(result.createInverse()); + } catch (final NoninvertibleTransformException e) { + throw new PAffineTransformException(e, result); + } + return result; + } + + // **************************************************************** + // Event Listeners - Methods for adding and removing event listeners + // from a node. + // + // Here methods are provided to add property change listeners and + // input event listeners. The property change listeners are notified + // when certain properties of this node change, and the input event + // listeners are notified when the nodes receives new key and mouse + // events. + // **************************************************************** + + /** + * Return the list of event listeners associated with this node. + * + * @return event listener list or null + */ + public EventListenerList getListenerList() { + return listenerList; + } + + /** + * Adds the specified input event listener to receive input events from this + * node. + * + * @param listener the new input listener + */ + public void addInputEventListener(final PInputEventListener listener) { + if (listenerList == null) { + listenerList = new EventListenerList(); + } + getListenerList().add(PInputEventListener.class, listener); + } + + /** + * Removes the specified input event listener so that it no longer receives + * input events from this node. + * + * @param listener the input listener to remove + */ + public void removeInputEventListener(final PInputEventListener listener) { + if (listenerList == null) { + return; + } + getListenerList().remove(PInputEventListener.class, listener); + if (listenerList.getListenerCount() == 0) { + listenerList = null; + } + } + + /** + * Add a PropertyChangeListener to the listener list. The listener is registered + * for all properties. See the fields in PNode and subclasses that start with + * PROPERTY_ to find out which properties exist. + * + * @param listener the PropertyChangeListener to be added + */ + public void addPropertyChangeListener(final PropertyChangeListener listener) { + if (changeSupport == null) { + changeSupport = new SwingPropertyChangeSupport(this); + } + changeSupport.addPropertyChangeListener(listener); + } + + /** + * Add a PropertyChangeListener for a specific property. The listener will be + * invoked only when a call on firePropertyChange names that specific property. + * See the fields in PNode and subclasses that start with PROPERTY_ to find out + * which properties are supported. + * + * @param propertyName The name of the property to listen on. + * @param listener the PropertyChangeListener to be added + */ + public void addPropertyChangeListener(final String propertyName, final PropertyChangeListener listener) { + if (listener == null) { + return; + } + if (changeSupport == null) { + changeSupport = new SwingPropertyChangeSupport(this); + } + changeSupport.addPropertyChangeListener(propertyName, listener); + } + + /** + * Remove a PropertyChangeListener from the listener list. This removes a + * PropertyChangeListener that was registered for all properties. + * + * @param listener the PropertyChangeListener to be removed + */ + public void removePropertyChangeListener(final PropertyChangeListener listener) { + if (changeSupport != null) { + changeSupport.removePropertyChangeListener(listener); + } + } + + /** + * Remove a PropertyChangeListener for a specific property. + * + * @param propertyName the name of the property that was listened on. + * @param listener the PropertyChangeListener to be removed + */ + public void removePropertyChangeListener(final String propertyName, final PropertyChangeListener listener) { + if (listener == null) { + return; + } + if (changeSupport == null) { + return; + } + changeSupport.removePropertyChangeListener(propertyName, listener); + } + + /** + * Return an array of all the property change listeners added to this node. + *

+ * If some listeners have been added with a named property, then the returned + * array will be a mixture of PropertyChangeListeners and + * PropertyChangeListenerProxys. If the calling method is + * interested in distinguishing the listeners then it must test each element to + * see if it is a PropertyChangeListenerProxy, perform the cast, + * and examine the parameter. + * + *

+	 * PropertyChangeListener[] listeners = bean.getPropertyChangeListeners();
+	 * for (int i = 0; i < listeners.length; i++) {
+	 * 	if (listeners[i] instanceof PropertyChangeListenerProxy) {
+	 * 		PropertyChangeListenerProxy proxy = (PropertyChangeListenerProxy) listeners[i];
+	 * 		if (proxy.getPropertyName().equals("foo")) {
+	 * 			// proxy is a PropertyChangeListener which was associated
+	 * 			// with the property named "foo"
+	 * 		}
+	 * 	}
+	 * }
+	 * 
+ * + * @since 3.0.1 + * @return all of the PropertyChangeListeners added or an empty + * array if no listeners have been added + */ + public PropertyChangeListener[] getPropertyChangeListeners() { + if (changeSupport == null) { + return new PropertyChangeListener[0]; + } + return changeSupport.getPropertyChangeListeners(); + } + + /** + * Return an array of all the property change listeners which have been + * associated with the named property. + * + * @since 3.0.1 + * @param propertyName the name of the property being listened to + * @return all of the PropertyChangeListeners associated with the + * named property. If no such listeners have been added, or if + * propertyName is null, an empty array is returned. + */ + public PropertyChangeListener[] getPropertyChangeListeners(final String propertyName) { + if (changeSupport == null) { + return new PropertyChangeListener[0]; + } + return changeSupport.getPropertyChangeListeners(propertyName); + } + + /** + * Return the propertyChangeParentMask that determines which property change + * events are forwared to this nodes parent so that its property change + * listeners will also be notified. + * + * @return mask used for deciding whether to bubble property changes up to + * parent + */ + public int getPropertyChangeParentMask() { + return propertyChangeParentMask; + } + + /** + * Set the propertyChangeParentMask that determines which property change events + * are forwared to this nodes parent so that its property change listeners will + * also be notified. + * + * @param propertyChangeParentMask new mask for property change bubble up + */ + public void setPropertyChangeParentMask(final int propertyChangeParentMask) { + this.propertyChangeParentMask = propertyChangeParentMask; + } + + /** + * Report a bound property update to any registered listeners. No event is fired + * if old and new are equal and non-null. If the propertyCode exists in this + * node's propertyChangeParentMask then a property change event will also be + * fired on this nodes parent. + * + * @param propertyCode The code of the property changed. + * @param propertyName The name of the property that was changed. + * @param oldValue The old value of the property. + * @param newValue The new value of the property. + */ + protected void firePropertyChange(final int propertyCode, final String propertyName, final Object oldValue, + final Object newValue) { + PropertyChangeEvent event = null; + + if (changeSupport != null) { + event = new PropertyChangeEvent(this, propertyName, oldValue, newValue); + changeSupport.firePropertyChange(event); + } + if (parent != null && (propertyCode & propertyChangeParentMask) != 0) { + if (event == null) { + event = new PropertyChangeEvent(this, propertyName, oldValue, newValue); + } + parent.fireChildPropertyChange(event, propertyCode); + } + } + + /** + * Called by child node to forward property change events up the node tree so + * that property change listeners registered with this node will be notified of + * property changes of its children nodes. For performance reason only + * propertyCodes listed in the propertyChangeParentMask are forwarded. + * + * @param event The property change event containing source node and + * changed values. + * @param propertyCode The code of the property changed. + */ + protected void fireChildPropertyChange(final PropertyChangeEvent event, final int propertyCode) { + if (changeSupport != null) { + changeSupport.firePropertyChange(event); + } + if (parent != null && (propertyCode & propertyChangeParentMask) != 0) { + parent.fireChildPropertyChange(event, propertyCode); + } + } + + // **************************************************************** + // Bounds Geometry - Methods for setting and querying the bounds + // of this node. + // + // The bounds of a node store the node's position and size in + // the nodes local coordinate system. Many node subclasses will need + // to override the setBounds method so that they can update their + // internal state appropriately. See PPath for an example. + // + // Since the bounds are stored in the local coordinate system + // they WILL NOT change if the node is scaled, translated, or rotated. + // + // The bounds may be accessed with either getBounds, or + // getBoundsReference. The former returns a copy of the bounds + // the latter returns a reference to the nodes bounds that should + // normally not be modified. If a node is marked as volatile then + // it may modify its bounds before returning them from getBoundsReference, + // otherwise it may not. + // **************************************************************** + + /** + * Return a copy of this node's bounds. These bounds are stored in the local + * coordinate system of this node and do not include the bounds of any of this + * node's children. + * + * @return copy of this node's local bounds + */ + public PBounds getBounds() { + return (PBounds) getBoundsReference().clone(); + } + + /** + * Return a direct reference to this node's bounds. These bounds are stored in + * the local coordinate system of this node and do not include the bounds of any + * of this node's children. The value returned should not be modified. + * + * @return direct reference to local bounds + */ + public PBounds getBoundsReference() { + return bounds; + } + + /** + * Notify this node that you will begin to repeatedly call setBounds + * . When you are done call endResizeBounds to let the node + * know that you are done. + */ + public void startResizeBounds() { + } + + /** + * Notify this node that you have finished a resize bounds sequence. + */ + public void endResizeBounds() { + } + + /** + * Set's this node's bounds left position, leaving y, width, and height + * unchanged. + * + * @param x new x position of bounds + * + * @return whether the change was successful + */ + public boolean setX(final double x) { + return setBounds(x, getY(), getWidth(), getHeight()); + } + + /** + * Set's this node's bounds top position, leaving x, width, and height + * unchanged. + * + * @param y new y position of bounds + * + * @return whether the change was successful + */ + public boolean setY(final double y) { + return setBounds(getX(), y, getWidth(), getHeight()); + } + + /** + * Set's this node's bounds width, leaving x, y, and height unchanged. + * + * @param width new width position of bounds + * + * @return whether the change was successful + */ + public boolean setWidth(final double width) { + return setBounds(getX(), getY(), width, getHeight()); + } + + /** + * Set's this node's bounds height, leaving x, y, and width unchanged. + * + * @param height new height position of bounds + * + * @return whether the change was successful + */ + public boolean setHeight(final double height) { + return setBounds(getX(), getY(), getWidth(), height); + } + + /** + * Set the bounds of this node to the given value. These bounds are stored in + * the local coordinate system of this node. + * + * @param newBounds bounds to apply to this node + * + * @return true if the bounds changed. + */ + public boolean setBounds(final Rectangle2D newBounds) { + return setBounds(newBounds.getX(), newBounds.getY(), newBounds.getWidth(), newBounds.getHeight()); + } + + /** + * Set the bounds of this node to the given position and size. These bounds are + * stored in the local coordinate system of this node. + * + * If the width or height is less then or equal to zero then the bound's empty + * bit will be set to true. + * + * Subclasses must call the super.setBounds() method. + * + * @param x x position of bounds + * @param y y position of bounds + * @param width width to apply to the bounds + * @param height height to apply to the bounds + * + * @return true if the bounds changed. + */ + public boolean setBounds(final double x, final double y, final double width, final double height) { + if (bounds.x != x || bounds.y != y || bounds.width != width || bounds.height != height) { + bounds.setRect(x, y, width, height); + + if (width <= 0 || height <= 0) { + bounds.reset(); + } + + internalUpdateBounds(x, y, width, height); + invalidatePaint(); + signalBoundsChanged(); + return true; + } + // Don't put any invalidating code here or else nodes with volatile + // bounds will + // create a soft infinite loop (calling Swing.invokeLater()) when they + // validate + // their bounds. + return false; + } + + /** + * Gives nodes a chance to update their internal structure before bounds changed + * notifications are sent. When this message is recived the nodes bounds field + * will contain the new value. + * + * See PPath for an example that uses this method. + * + * @param x x position of bounds + * @param y y position of bounds + * @param width width to apply to the bounds + * @param height height to apply to the bounds + */ + protected void internalUpdateBounds(final double x, final double y, final double width, final double height) { + } + + /** + * Set the empty bit of this bounds to true. + */ + public void resetBounds() { + setBounds(0, 0, 0, 0); + } + + /** + * Return the x position (in local coords) of this node's bounds. + * + * @return local x position of bounds + */ + public double getX() { + return getBoundsReference().getX(); + } + + /** + * Return the y position (in local coords) of this node's bounds. + * + * @return local y position of bounds + */ + public double getY() { + return getBoundsReference().getY(); + } + + /** + * Return the width (in local coords) of this node's bounds. + * + * @return local width of bounds + */ + public double getWidth() { + return getBoundsReference().getWidth(); + } + + /** + * Return the height (in local coords) of this node's bounds. + * + * @return local width of bounds + */ + public double getHeight() { + return getBoundsReference().getHeight(); + } + + /** + * Return a copy of the bounds of this node in the global coordinate system. + * + * @return the bounds in global coordinate system. + */ + public PBounds getGlobalBounds() { + return (PBounds) localToGlobal(getBounds()); + } + + /** + * Center the bounds of this node so that they are centered on the given point + * specified on the local coordinates of this node. Note that this method will + * modify the nodes bounds, while centerFullBoundsOnPoint will modify the nodes + * transform. + * + * @param localX x position of point around which to center bounds + * @param localY y position of point around which to center bounds + * + * @return true if the bounds changed. + */ + public boolean centerBoundsOnPoint(final double localX, final double localY) { + final double dx = localX - bounds.getCenterX(); + final double dy = localY - bounds.getCenterY(); + return setBounds(bounds.x + dx, bounds.y + dy, bounds.width, bounds.height); + } + + /** + * Center the full bounds of this node so that they are centered on the given + * point specified on the local coordinates of this nodes parent. Note that this + * method will modify the nodes transform, while centerBoundsOnPoint will modify + * the nodes bounds. + * + * @param parentX x position around which to center full bounds + * @param parentY y position around which to center full bounds + */ + public void centerFullBoundsOnPoint(final double parentX, final double parentY) { + final double dx = parentX - getFullBoundsReference().getCenterX(); + final double dy = parentY - getFullBoundsReference().getCenterY(); + offset(dx, dy); + } + + /** + * Return true if this node intersects the given rectangle specified in local + * bounds. If the geometry of this node is complex this method can become + * expensive, it is therefore recommended that fullIntersects is + * used for quick rejects before calling this method. + * + * @param localBounds the bounds to test for intersection against + * @return true if the given rectangle intersects this nodes geometry. + */ + public boolean intersects(final Rectangle2D localBounds) { + if (localBounds == null) { + return true; + } + return getBoundsReference().intersects(localBounds); + } + + // **************************************************************** + // Full Bounds - Methods for computing and querying the + // full bounds of this node. + // + // The full bounds of a node store the nodes bounds + // together with the union of the bounds of all the + // node's descendants. The full bounds are stored in the parent + // coordinate system of this node, the full bounds DOES change + // when you translate, scale, or rotate this node. + // + // The full bounds may be accessed with either getFullBounds, or + // getFullBoundsReference. The former returns a copy of the full bounds + // the latter returns a reference to the node's full bounds that should + // not be modified. + // **************************************************************** + + /** + * Return a copy of this node's full bounds. These bounds are stored in the + * parent coordinate system of this node and they include the union of this + * node's bounds and all the bounds of it's descendants. + * + * @return a copy of this node's full bounds. + */ + public PBounds getFullBounds() { + return (PBounds) getFullBoundsReference().clone(); + } + + /** + * Return a reference to this node's full bounds cache. These bounds are stored + * in the parent coordinate system of this node and they include the union of + * this node's bounds and all the bounds of it's descendants. The bounds + * returned by this method should not be modified. + * + * @return a reference to this node's full bounds cache. + */ + public PBounds getFullBoundsReference() { + validateFullBounds(); + return fullBoundsCache; + } + + /** + * Compute and return the full bounds of this node. If the dstBounds parameter + * is not null then it will be used to return the results instead of creating a + * new PBounds. + * + * @param dstBounds if not null the new bounds will be stored here + * @return the full bounds in the parent coordinate system of this node + */ + public PBounds computeFullBounds(final PBounds dstBounds) { + final PBounds result = getUnionOfChildrenBounds(dstBounds); + result.add(getBoundsReference()); + localToParent(result); + return result; + } + + /** + * Compute and return the union of the full bounds of all the children of this + * node. If the dstBounds parameter is not null then it will be used to return + * the results instead of creating a new PBounds. + * + * @param dstBounds if not null the new bounds will be stored here + * @return union of children bounds + */ + public PBounds getUnionOfChildrenBounds(final PBounds dstBounds) { + PBounds resultBounds; + if (dstBounds == null) { + resultBounds = new PBounds(); + } else { + resultBounds = dstBounds; + resultBounds.resetToZero(); + } + + final int count = getChildrenCount(); + for (int i = 0; i < count; i++) { + final PNode each = (PNode) children.get(i); + resultBounds.add(each.getFullBoundsReference()); + } + + return resultBounds; + } + + /** + * Return a copy of the full bounds of this node in the global coordinate + * system. + * + * @return the full bounds in global coordinate system. + */ + public PBounds getGlobalFullBounds() { + final PBounds b = getFullBounds(); + if (parent != null) { + parent.localToGlobal(b); + } + return b; + } + + /** + * Return true if the full bounds of this node intersects with the specified + * bounds. + * + * @param parentBounds the bounds to test for intersection against (specified in + * parent's coordinate system) + * @return true if this nodes full bounds intersect the given bounds. + */ + public boolean fullIntersects(final Rectangle2D parentBounds) { + if (parentBounds == null) { + return true; + } + return getFullBoundsReference().intersects(parentBounds); + } + + // **************************************************************** + // Bounds Damage Management - Methods used to invalidate and validate + // the bounds of nodes. + // **************************************************************** + + /** + * Return true if this nodes bounds may change at any time. The default behavior + * is to return false, subclasses that override this method to return true + * should also override getBoundsReference() and compute their volatile bounds + * there before returning the reference. + * + * @return true if this node has volatile bounds + */ + protected boolean getBoundsVolatile() { + return false; + } + + /** + * Return true if this node has a child with volatile bounds. + * + * @return true if this node has a child with volatile bounds + */ + protected boolean getChildBoundsVolatile() { + return childBoundsVolatile; + } + + /** + * Set if this node has a child with volatile bounds. This should normally be + * managed automatically by the bounds validation process. + * + * @param childBoundsVolatile true if this node has a descendant with volatile + * bounds + */ + protected void setChildBoundsVolatile(final boolean childBoundsVolatile) { + this.childBoundsVolatile = childBoundsVolatile; + } + + /** + * Return true if this node's bounds have recently changed. This flag will be + * reset on the next call of validateFullBounds. + * + * @return true if this node's bounds have changed. + */ + protected boolean getBoundsChanged() { + return boundsChanged; + } + + /** + * Set the bounds chnaged flag. This flag will be reset on the next call of + * validateFullBounds. + * + * @param boundsChanged true if this nodes bounds have changed. + */ + protected void setBoundsChanged(final boolean boundsChanged) { + this.boundsChanged = boundsChanged; + } + + /** + * Return true if the full bounds of this node are invalid. This means that the + * full bounds of this node have changed and need to be recomputed. + * + * @return true if the full bounds of this node are invalid + */ + protected boolean getFullBoundsInvalid() { + return fullBoundsInvalid; + } + + /** + * Set the full bounds invalid flag. This flag is set when the full bounds of + * this node need to be recomputed as is the case when this node is transformed + * or when one of this node's children changes geometry. + * + * @param fullBoundsInvalid true=invalid, false=valid + */ + protected void setFullBoundsInvalid(final boolean fullBoundsInvalid) { + this.fullBoundsInvalid = fullBoundsInvalid; + } + + /** + * Return true if one of this node's descendants has invalid bounds. + * + * @return whether child bounds are invalid + */ + protected boolean getChildBoundsInvalid() { + return childBoundsInvalid; + } + + /** + * Set the flag indicating that one of this node's descendants has invalid + * bounds. + * + * @param childBoundsInvalid true=invalid, false=valid + */ + protected void setChildBoundsInvalid(final boolean childBoundsInvalid) { + this.childBoundsInvalid = childBoundsInvalid; + } + + /** + * This method should be called when the bounds of this node are changed. It + * invalidates the full bounds of this node, and also notifies each of this + * nodes children that their parent's bounds have changed. As a result of this + * method getting called this nodes layoutChildren will be called. + */ + public void signalBoundsChanged() { + invalidateFullBounds(); + setBoundsChanged(true); + firePropertyChange(PROPERTY_CODE_BOUNDS, PROPERTY_BOUNDS, null, bounds); + + final int count = getChildrenCount(); + for (int i = 0; i < count; i++) { + final PNode each = (PNode) children.get(i); + each.parentBoundsChanged(); + } + } + + /** + * Invalidate this node's layout, so that later layoutChildren will get called. + */ + public void invalidateLayout() { + invalidateFullBounds(); + } + + /** + * A notification that the bounds of this node's parent have changed. + */ + protected void parentBoundsChanged() { + } + + /** + * Invalidates the full bounds of this node, and sets the child bounds invalid + * flag on each of this node's ancestors. + */ + public void invalidateFullBounds() { + setFullBoundsInvalid(true); + + PNode n = parent; + while (n != null && !n.getChildBoundsInvalid()) { + n.setChildBoundsInvalid(true); + n = n.parent; + } + + if (SCENE_GRAPH_DELEGATE != null) { + SCENE_GRAPH_DELEGATE.nodeFullBoundsInvalidated(this); + } + } + + /** + * This method is called to validate the bounds of this node and all of its + * descendants. It returns true if this nodes bounds or the bounds of any of its + * descendants are marked as volatile. + * + * @return true if this node or any of its descendants have volatile bounds + */ + protected boolean validateFullBounds() { + final boolean boundsVolatile = getBoundsVolatile(); + + // 1. Only compute new bounds if invalid flags are set. + if (fullBoundsInvalid || childBoundsInvalid || boundsVolatile || childBoundsVolatile) { + + // 2. If my bounds are volatile and they have not been changed then + // signal a change. + // + // For most cases this will do nothing, but if a nodes bounds depend + // on its model, then + // validate bounds has the responsibility of making the bounds match + // the models value. + // For example PPaths validateBounds method makes sure that the + // bounds are equal to the + // bounds of the GeneralPath model. + if (boundsVolatile && !boundsChanged) { + signalBoundsChanged(); + } + + // 3. If the bounds of on of my decendents are invalidate then + // validate the bounds of all of my children. + if (childBoundsInvalid || childBoundsVolatile) { + childBoundsVolatile = false; + final int count = getChildrenCount(); + for (int i = 0; i < count; i++) { + final PNode each = (PNode) children.get(i); + childBoundsVolatile |= each.validateFullBounds(); + } + } + + // 4. Now that my children's bounds are valid and my own bounds are + // valid run any layout algorithm here. Note that if you try to + // layout volatile + // children piccolo will most likely start a "soft" infinite loop. + // It won't freeze + // your program, but it will make an infinite number of calls to + // SwingUtilities + // invoke later. You don't want to do that. + layoutChildren(); + + // 5. If the full bounds cache is invalid then recompute the full + // bounds cache here after our own bounds and the children's bounds + // have been computed above. + if (fullBoundsInvalid) { + final double oldX = fullBoundsCache.x; + final double oldY = fullBoundsCache.y; + final double oldWidth = fullBoundsCache.width; + final double oldHeight = fullBoundsCache.height; + final boolean oldEmpty = fullBoundsCache.isEmpty(); + + // 6. This will call getFullBoundsReference on all of the + // children. So if the above + // layoutChildren method changed the bounds of any of the + // children they will be + // validated again here. + fullBoundsCache = computeFullBounds(fullBoundsCache); + + final boolean fullBoundsChanged = fullBoundsCache.x != oldX || fullBoundsCache.y != oldY + || fullBoundsCache.width != oldWidth || fullBoundsCache.height != oldHeight + || fullBoundsCache.isEmpty() != oldEmpty; + + // 7. If the new full bounds cache differs from the previous + // cache then + // tell our parent to invalidate their full bounds. This is how + // bounds changes + // deep in the tree percolate up. + if (fullBoundsChanged) { + if (parent != null) { + parent.invalidateFullBounds(); + } + firePropertyChange(PROPERTY_CODE_FULL_BOUNDS, PROPERTY_FULL_BOUNDS, null, fullBoundsCache); + + // 8. If our paint was invalid make sure to repaint our old + // full bounds. The + // new bounds will be computed later in the validatePaint + // pass. + if (paintInvalid && !oldEmpty) { + TEMP_REPAINT_BOUNDS.setRect(oldX, oldY, oldWidth, oldHeight); + repaintFrom(TEMP_REPAINT_BOUNDS, this); + } + } + } + + // 9. Clear the invalid bounds flags. + boundsChanged = false; + fullBoundsInvalid = false; + childBoundsInvalid = false; + } + + return boundsVolatile || childBoundsVolatile; + } + + /** + * Nodes that apply layout constraints to their children should override this + * method and do the layout there. + */ + protected void layoutChildren() { + } + + // **************************************************************** + // Node Transform - Methods to manipulate the node's transform. + // + // Each node has a transform that is used to define the nodes + // local coordinate system. IE it is applied before picking and + // rendering the node. + // + // The usual way to move nodes about on the canvas is to manipulate + // this transform, as opposed to changing the bounds of the + // node. + // + // Since this transform defines the local coordinate system of this + // node the following methods with affect the global position both + // this node and all of its descendants. + // **************************************************************** + + /** + * Returns the rotation applied by this node's transform in radians. This + * rotation affects this node and all its descendants. The value returned will + * be between 0 and 2pi radians. + * + * @return rotation in radians. + */ + public double getRotation() { + if (transform == null) { + return 0; + } + return transform.getRotation(); + } + + /** + * Sets the rotation of this nodes transform in radians. This will affect this + * node and all its descendents. + * + * @param theta rotation in radians + */ + public void setRotation(final double theta) { + rotate(theta - getRotation()); + } + + /** + * Rotates this node by theta (in radians) about the 0,0 point. This will affect + * this node and all its descendants. + * + * @param theta the amount to rotate by in radians + */ + public void rotate(final double theta) { + rotateAboutPoint(theta, 0, 0); + } + + /** + * Rotates this node by theta (in radians), and then translates the node so that + * the x, y position of its fullBounds stays constant. + * + * @param theta the amount to rotate by in radians + */ + public void rotateInPlace(final double theta) { + PBounds b = getFullBoundsReference(); + final double px = b.x; + final double py = b.y; + rotateAboutPoint(theta, 0, 0); + b = getFullBoundsReference(); + offset(px - b.x, py - b.y); + } + + /** + * Rotates this node by theta (in radians) about the given point. This will + * affect this node and all its descendants. + * + * @param theta the amount to rotate by in radians + * @param point the point about which to rotate + */ + public void rotateAboutPoint(final double theta, final Point2D point) { + rotateAboutPoint(theta, point.getX(), point.getY()); + } + + /** + * Rotates this node by theta (in radians) about the given point. This will + * affect this node and all its descendants. + * + * @param theta the amount to rotate by in radians + * @param x the x coordinate of the point around which to rotate + * @param y the y coordinate of the point around which to rotate + */ + public void rotateAboutPoint(final double theta, final double x, final double y) { + getTransformReference(true).rotate(theta, x, y); + invalidatePaint(); + invalidateFullBounds(); + firePropertyChange(PROPERTY_CODE_TRANSFORM, PROPERTY_TRANSFORM, null, transform); + } + + /** + * Return the total amount of rotation applied to this node by its own transform + * together with the transforms of all its ancestors. The value returned will be + * between 0 and 2pi radians. + * + * @return the total amount of rotation applied to this node in radians + */ + public double getGlobalRotation() { + return getLocalToGlobalTransform(null).getRotation(); + } + + /** + * Set the global rotation (in radians) of this node. This is implemented by + * rotating this nodes transform the required amount so that the nodes global + * rotation is as requested. + * + * @param theta the amount to rotate by in radians relative to the global + * coordinate system. + */ + public void setGlobalRotation(final double theta) { + if (parent != null) { + setRotation(theta - parent.getGlobalRotation()); + } else { + setRotation(theta); + } + } + + /** + * Return the scale applied by this node's transform. The scale is effecting + * this node and all its descendants. + * + * @return scale applied by this nodes transform. + */ + public double getScale() { + if (transform == null) { + return 1; + } + return transform.getScale(); + } + + /** + * Set the scale of this node's transform. The scale will affect this node and + * all its descendants. + * + * @param scale the scale to set the transform to + */ + public void setScale(final double scale) { + if (scale == 0) { + throw new RuntimeException("Can't set scale to 0"); + } + scale(scale / getScale()); + } + + /** + * Scale this nodes transform by the given amount. This will affect this node + * and all of its descendants. + * + * @param scale the amount to scale by + */ + public void scale(final double scale) { + scaleAboutPoint(scale, 0, 0); + } + + /** + * Scale this nodes transform by the given amount about the specified point. + * This will affect this node and all of its descendants. + * + * @param scale the amount to scale by + * @param point the point to scale about + */ + public void scaleAboutPoint(final double scale, final Point2D point) { + scaleAboutPoint(scale, point.getX(), point.getY()); + } + + /** + * Scale this nodes transform by the given amount about the specified point. + * This will affect this node and all of its descendants. + * + * @param scale the amount to scale by + * @param x the x coordinate of the point around which to scale + * @param y the y coordinate of the point around which to scale + */ + public void scaleAboutPoint(final double scale, final double x, final double y) { + getTransformReference(true).scaleAboutPoint(scale, x, y); + invalidatePaint(); + invalidateFullBounds(); + firePropertyChange(PROPERTY_CODE_TRANSFORM, PROPERTY_TRANSFORM, null, transform); + } + + /** + * Return the global scale that is being applied to this node by its transform + * together with the transforms of all its ancestors. + * + * @return global scale of this node + */ + public double getGlobalScale() { + return getLocalToGlobalTransform(null).getScale(); + } + + /** + * Set the global scale of this node. This is implemented by scaling this nodes + * transform the required amount so that the nodes global scale is as requested. + * + * @param scale the desired global scale + */ + public void setGlobalScale(final double scale) { + if (parent != null) { + setScale(scale / parent.getGlobalScale()); + } else { + setScale(scale); + } + } + + /** + * Returns the x offset of this node as applied by its transform. + * + * @return x offset of this node as applied by its transform + */ + public double getXOffset() { + if (transform == null) { + return 0; + } + return transform.getTranslateX(); + } + + /** + * Returns the y offset of this node as applied by its transform. + * + * @return y offset of this node as applied by its transform + */ + public double getYOffset() { + if (transform == null) { + return 0; + } + return transform.getTranslateY(); + } + + /** + * Return the offset that is being applied to this node by its transform. This + * offset effects this node and all of its descendants and is specified in the + * parent coordinate system. This returns the values that are in the m02 and m12 + * positions in the affine transform. + * + * @return a point representing the x and y offset + */ + public Point2D getOffset() { + if (transform == null) { + return new Point2D.Double(); + } + return new Point2D.Double(transform.getTranslateX(), transform.getTranslateY()); + } + + /** + * Set the offset that is being applied to this node by its transform. This + * offset effects this node and all of its descendants and is specified in the + * nodes parent coordinate system. This directly sets the values of the m02 and + * m12 positions in the affine transform. Unlike "PNode.translate()" it is not + * effected by the transforms scale. + * + * @param point value of new offset + */ + public void setOffset(final Point2D point) { + setOffset(point.getX(), point.getY()); + } + + /** + * Set the offset that is being applied to this node by its transform. This + * offset effects this node and all of its descendants and is specified in the + * nodes parent coordinate system. This directly sets the values of the m02 and + * m12 positions in the affine transform. Unlike "PNode.translate()" it is not + * effected by the transforms scale. + * + * @param x amount of x offset + * @param y amount of y offset + */ + public void setOffset(final double x, final double y) { + getTransformReference(true).setOffset(x, y); + invalidatePaint(); + invalidateFullBounds(); + firePropertyChange(PROPERTY_CODE_TRANSFORM, PROPERTY_TRANSFORM, null, transform); + } + + /** + * Offset this node relative to the parents coordinate system, and is NOT + * effected by this nodes current scale or rotation. This is implemented by + * directly adding dx to the m02 position and dy to the m12 position in the + * affine transform. + * + * @param dx amount to add to this nodes current x Offset + * @param dy amount to add to this nodes current y Offset + */ + public void offset(final double dx, final double dy) { + getTransformReference(true); + setOffset(transform.getTranslateX() + dx, transform.getTranslateY() + dy); + } + + /** + * Translate this node's transform by the given amount, using the standard + * affine transform translate method. This translation effects this node and all + * of its descendants. + * + * @param dx amount to add to this nodes current x translation + * @param dy amount to add to this nodes current y translation + */ + public void translate(final double dx, final double dy) { + getTransformReference(true).translate(dx, dy); + invalidatePaint(); + invalidateFullBounds(); + firePropertyChange(PROPERTY_CODE_TRANSFORM, PROPERTY_TRANSFORM, null, transform); + } + + /** + * Return the global translation that is being applied to this node by its + * transform together with the transforms of all its ancestors. + * + * @return the global translation applied to this node + */ + public Point2D getGlobalTranslation() { + final Point2D p = getOffset(); + if (parent != null) { + parent.localToGlobal(p); + } + return p; + } + + /** + * Set the global translation of this node. This is implemented by translating + * this nodes transform the required amount so that the nodes global scale is as + * requested. + * + * @param globalPoint the desired global translation + */ + public void setGlobalTranslation(final Point2D globalPoint) { + if (parent != null) { + parent.getGlobalToLocalTransform(null).transform(globalPoint, globalPoint); + } + setOffset(globalPoint); + } + + /** + * Transform this nodes transform by the given transform. + * + * @param aTransform the transform to apply. + */ + public void transformBy(final AffineTransform aTransform) { + getTransformReference(true).concatenate(aTransform); + invalidatePaint(); + invalidateFullBounds(); + firePropertyChange(PROPERTY_CODE_TRANSFORM, PROPERTY_TRANSFORM, null, transform); + } + + /** + * Linearly interpolates between a and b, based on t. Specifically, it computes + * lerp(a, b, t) = a + t*(b - a). This produces a result that changes from a + * (when t = 0) to b (when t = 1). + * + * @param t variable 'time' parameter + * @param a from point + * @param b to Point + * + * @return linear interpolation between and b at time interval t (given as # + * between 0f and 1f) + */ + public static double lerp(final double t, final double a, final double b) { + return a + t * (b - a); + } + + /** + * This will calculate the necessary transform in order to make this node appear + * at a particular position relative to the specified bounding box. The source + * point specifies a point in the unit square (0, 0) - (1, 1) that represents an + * anchor point on the corresponding node to this transform. The destination + * point specifies an anchor point on the reference node. The position method + * then computes the transform that results in transforming this node so that + * the source anchor point coincides with the reference anchor point. This can + * be useful for layout algorithms as it is straightforward to position one + * object relative to another. + *

+ * For example, If you have two nodes, A and B, and you call + * + *

+	 * Point2D srcPt = new Point2D.Double(1.0, 0.0);
+	 * Point2D destPt = new Point2D.Double(0.0, 0.0);
+	 * A.position(srcPt, destPt, B.getGlobalBounds(), 750, null);
+	 * 
+ * + * The result is that A will move so that its upper-right corner is at the same + * place as the upper-left corner of B, and the transition will be smoothly + * animated over a period of 750 milliseconds. + * + * @since 1.3 + * @param srcPt The anchor point on this transform's node (normalized to a + * unit square) + * @param destPt The anchor point on destination bounds (normalized to a + * unit square) + * @param destBounds The bounds (in global coordinates) used to calculate this + * transform's node + * @param millis Number of milliseconds over which to perform the animation + * + * @return newly scheduled activity or node if activity could not be scheduled + */ + public PActivity animateToRelativePosition(final Point2D srcPt, final Point2D destPt, final Rectangle2D destBounds, + final int millis) { + double srcx, srcy; + double destx, desty; + double dx, dy; + Point2D pt1, pt2; + + if (parent == null) { + return null; + } else { + // First compute translation amount in global coordinates + final Rectangle2D srcBounds = getGlobalFullBounds(); + srcx = lerp(srcPt.getX(), srcBounds.getX(), srcBounds.getX() + srcBounds.getWidth()); + srcy = lerp(srcPt.getY(), srcBounds.getY(), srcBounds.getY() + srcBounds.getHeight()); + destx = lerp(destPt.getX(), destBounds.getX(), destBounds.getX() + destBounds.getWidth()); + desty = lerp(destPt.getY(), destBounds.getY(), destBounds.getY() + destBounds.getHeight()); + + // Convert vector to local coordinates + pt1 = new Point2D.Double(srcx, srcy); + globalToLocal(pt1); + pt2 = new Point2D.Double(destx, desty); + globalToLocal(pt2); + dx = pt2.getX() - pt1.getX(); + dy = pt2.getY() - pt1.getY(); + + // Finally, animate change + final PAffineTransform at = new PAffineTransform(getTransformReference(true)); + at.translate(dx, dy); + return animateToTransform(at, millis); + } + } + + /** + * Return a copy of the transform associated with this node. + * + * @return copy of this node's transform + */ + public PAffineTransform getTransform() { + if (transform == null) { + return new PAffineTransform(); + } else { + return (PAffineTransform) transform.clone(); + } + } + + /** + * Return a reference to the transform associated with this node. This returned + * transform should not be modified. PNode transforms are created lazily when + * needed. If you access the transform reference before the transform has been + * created it may return null. The createNewTransformIfNull parameter is used to + * specify that the PNode should create a new transform (and assign that + * transform to the nodes local transform variable) instead of returning null. + * + * @param createNewTransformIfNull if the transform has not been initialised, + * should it be? + * + * @return reference to this node's transform + */ + public PAffineTransform getTransformReference(final boolean createNewTransformIfNull) { + if (transform == null && createNewTransformIfNull) { + transform = new PAffineTransform(); + } + return transform; + } + + /** + * Return an inverted copy of the transform associated with this node. + * + * @return inverted copy of this node's transform + */ + public PAffineTransform getInverseTransform() { + if (transform == null) { + return new PAffineTransform(); + } + + try { + return new PAffineTransform(transform.createInverse()); + } catch (final NoninvertibleTransformException e) { + throw new PAffineTransformException(e, transform); + } + } + + /** + * Set the transform applied to this node. + * + * @param transform the new transform value + */ + public void setTransform(final AffineTransform transform) { + if (transform == null) { + this.transform = null; + } else { + getTransformReference(true).setTransform(transform); + } + + invalidatePaint(); + invalidateFullBounds(); + firePropertyChange(PROPERTY_CODE_TRANSFORM, PROPERTY_TRANSFORM, null, this.transform); + } + + // **************************************************************** + // Paint Damage Management - Methods used to invalidate the areas of + // the screen that this node appears in so that they will later get + // painted. + // + // Generally you will not need to call these invalidate methods + // when starting out with Piccolo2d because methods such as setPaint + // already automatically call them for you. You will need to call + // them when you start creating your own nodes. + // + // When you do create you own nodes the only method that you will + // normally need to call is invalidatePaint. This method marks the + // nodes as having invalid paint, the root node's UI cycle will then + // later discover this damage and report it to the Java repaint manager. + // + // Repainting is normally done with PNode.invalidatePaint() instead of + // directly calling PNode.repaint() because PNode.repaint() requires + // the nodes bounds to be computed right away. But with invalidatePaint + // the bounds computation can be delayed until the end of the root's UI + // cycle, and this can add up to a bit savings when modifying a + // large number of nodes all at once. + // + // The other methods here will rarely be called except internally + // from the framework. + // **************************************************************** + + /** + * Return true if this nodes paint is invalid, in which case the node needs to + * be repainted. + * + * @return true if this node needs to be repainted + */ + public boolean getPaintInvalid() { + return paintInvalid; + } + + /** + * Mark this node as having invalid paint. If this is set the node will later be + * repainted. Node this method is most often used internally. + * + * @param paintInvalid true if this node should be repainted + */ + public void setPaintInvalid(final boolean paintInvalid) { + this.paintInvalid = paintInvalid; + } + + /** + * Return true if this node has a child with invalid paint. + * + * @return true if this node has a child with invalid paint + */ + public boolean getChildPaintInvalid() { + return childPaintInvalid; + } + + /** + * Mark this node as having a child with invalid paint. + * + * @param childPaintInvalid true if this node has a child with invalid paint + */ + public void setChildPaintInvalid(final boolean childPaintInvalid) { + this.childPaintInvalid = childPaintInvalid; + } + + /** + * Invalidate this node's paint, and mark all of its ancestors as having a node + * with invalid paint. + */ + public void invalidatePaint() { + setPaintInvalid(true); + + PNode n = parent; + while (n != null && !n.getChildPaintInvalid()) { + n.setChildPaintInvalid(true); + n = n.parent; + } + + if (SCENE_GRAPH_DELEGATE != null) { + SCENE_GRAPH_DELEGATE.nodePaintInvalidated(this); + } + } + + /** + * Repaint this node and any of its descendants if they have invalid paint. + */ + public void validateFullPaint() { + if (getPaintInvalid()) { + repaint(); + setPaintInvalid(false); + } + + if (getChildPaintInvalid()) { + final int count = getChildrenCount(); + for (int i = 0; i < count; i++) { + final PNode each = (PNode) children.get(i); + each.validateFullPaint(); + } + setChildPaintInvalid(false); + } + } + + /** + * Mark the area on the screen represented by this nodes full bounds as needing + * a repaint. + */ + public void repaint() { + TEMP_REPAINT_BOUNDS.setRect(getFullBoundsReference()); + repaintFrom(TEMP_REPAINT_BOUNDS, this); + } + + /** + * Pass the given repaint request up the tree, so that any cameras can + * invalidate that region on their associated canvas. + * + * @param localBounds the bounds to repaint + * @param childOrThis if childOrThis does not equal this then this nodes + * transform will be applied to the localBounds param + */ + public void repaintFrom(final PBounds localBounds, final PNode childOrThis) { + if (parent != null) { + if (childOrThis != this) { + localToParent(localBounds); + } else if (!getVisible()) { + return; + } + parent.repaintFrom(localBounds, this); + } + } + + // **************************************************************** + // Occluding - Methods to support occluding optimisation. Not yet + // complete. + // **************************************************************** + + /** + * Returns whether this node is Opaque. + * + * @param boundary boundary to check and see if this node covers completely. + * + * @return true if opaque + */ + public boolean isOpaque(final Rectangle2D boundary) { + return false; + } + + /** + * Returns whether this node has been flagged as occluded. + * + * @return true if occluded + */ + public boolean getOccluded() { + return occluded; + } + + /** + * Flags this node as occluded. + * + * @param occluded new value for occluded + */ + public void setOccluded(final boolean occluded) { + this.occluded = occluded; + } + + // **************************************************************** + // Painting - Methods for painting this node and its children + // + // Painting is how a node defines its visual representation on the + // screen, and is done in the local coordinate system of the node. + // + // The default painting behavior is to first paint the node, and + // then paint the node's children on top of the node. If a node + // needs wants specialised painting behavior it can override: + // + // paint() - Painting here will happen before the children + // are painted, so the children will be painted on top of painting done + // here. + // paintAfterChildren() - Painting here will happen after the children + // are painted, so it will paint on top of them. + // + // Note that you should not normally need to override fullPaint(). + // + // The visible flag can be used to make a node invisible so that + // it will never get painted. + // **************************************************************** + + /** + * Return true if this node is visible, that is if it will paint itself and + * descendants. + * + * @return true if this node and its descendants are visible. + */ + public boolean getVisible() { + return visible; + } + + /** + * Set the visibility of this node and its descendants. + * + * @param isVisible true if this node and its descendants are visible + */ + public void setVisible(final boolean isVisible) { + if (getVisible() != isVisible) { + if (!isVisible) { + repaint(); + } + visible = isVisible; + firePropertyChange(PROPERTY_CODE_VISIBLE, PROPERTY_VISIBLE, null, null); + invalidatePaint(); + } + } + + /** + * Return the paint used while painting this node. This value may be null. + * + * @return the paint used while painting this node. + */ + public Paint getPaint() { + return paint; + } + + /** + * Set the paint used to paint this node, which may be null. + * + * @param newPaint paint that this node should use when painting itself. + */ + public void setPaint(final Paint newPaint) { + if (paint == newPaint) { + return; + } + + final Paint oldPaint = paint; + paint = newPaint; + invalidatePaint(); + firePropertyChange(PROPERTY_CODE_PAINT, PROPERTY_PAINT, oldPaint, paint); + } + + /** + * Return the transparency used when painting this node. Note that this + * transparency is also applied to all of the node's descendants. + * + * @return how transparent this node is 0f = completely transparent, 1f = + * completely opaque + */ + public float getTransparency() { + return transparency; + } + + /** + * Set the transparency used to paint this node. Note that this transparency + * applies to this node and all of its descendants. + * + * @param newTransparency transparency value for this node. 0f = fully + * transparent, 1f = fully opaque + */ + public void setTransparency(final float newTransparency) { + if (Math.abs(transparency - newTransparency) > TRANSPARENCY_RESOLUTION) { + final float oldTransparency = transparency; + transparency = newTransparency; + invalidatePaint(); + firePropertyChange(PROPERTY_CODE_TRANSPARENCY, PROPERTY_TRANSPARENCY, Float.valueOf(oldTransparency), + Float.valueOf(newTransparency)); + } + } + + /** + * Paint this node behind any of its children nodes. Subclasses that define a + * different appearance should override this method and paint themselves there. + * + * @param paintContext the paint context to use for painting the node + */ + protected void paint(final PPaintContext paintContext) { + if (paint != null) { + final Graphics2D g2 = paintContext.getGraphics(); + g2.setPaint(paint); + g2.fill(getBoundsReference()); + } + } + + /** + * Paint this node and all of its descendants. Most subclasses do not need to + * override this method, they should override paint or + * paintAfterChildren instead. + * + * @param paintContext the paint context to use for painting this node and its + * children + */ + public void fullPaint(final PPaintContext paintContext) { + if (getVisible() && fullIntersects(paintContext.getLocalClip())) { + paintContext.pushTransform(transform); + paintContext.pushTransparency(transparency); + + if (!getOccluded()) { + paint(paintContext); + } + + final int count = getChildrenCount(); + for (int i = 0; i < count; i++) { + final PNode each = (PNode) children.get(i); + each.fullPaint(paintContext); + } + + paintAfterChildren(paintContext); + + paintContext.popTransparency(transparency); + paintContext.popTransform(transform); + } + } + + /** + * Subclasses that wish to do additional painting after their children are + * painted should override this method and do that painting here. + * + * @param paintContext the paint context to sue for painting after the children + * are painted + */ + protected void paintAfterChildren(final PPaintContext paintContext) { + } + + /** + * Return a new Image representing this node and all of its children. The image + * size will be equal to the size of this nodes full bounds. + * + * @return a new image representing this node and its descendants + */ + public Image toImage() { + final PBounds b = getFullBoundsReference(); + return toImage((int) Math.ceil(b.getWidth()), (int) Math.ceil(b.getHeight()), null); + } + + /** + * Return a new Image of the requested size representing this node and all of + * its children. If backGroundPaint is null the resulting image will have + * transparent regions, otherwise those regions will be filled with the + * backgroundPaint. + * + * @param width pixel width of the resulting image + * @param height pixel height of the resulting image + * @param backgroundPaint paint to fill the image with before drawing this node, + * may be null + * + * @return a new image representing this node and its descendants + */ + public Image toImage(final int width, final int height, final Paint backgroundPaint) { + BufferedImage result; + + if (GraphicsEnvironment.isHeadless()) { + result = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + } else { + final GraphicsConfiguration graphicsConfiguration = GraphicsEnvironment.getLocalGraphicsEnvironment() + .getDefaultScreenDevice().getDefaultConfiguration(); + result = graphicsConfiguration.createCompatibleImage(width, height, Transparency.TRANSLUCENT); + } + + return toImage(result, backgroundPaint); + } + + /** + * Paint a representation of this node into the specified buffered image. If + * background, paint is null, then the image will not be filled with a color + * prior to rendering + * + * @param image Image onto which this node will be painted + * @param backGroundPaint will fill background of image with this. May be null. + * @return a rendering of this image and its descendants onto the specified + * image + */ + public Image toImage(final BufferedImage image, final Paint backGroundPaint) { + return toImage(image, backGroundPaint, FILL_STRATEGY_ASPECT_FIT); + } + + /** + * Paint a representation of this node into the specified buffered image. If + * background, paint is null, then the image will not be filled with a color + * prior to rendering + * + * @since 1.3 + * @param image Image onto which this node will be painted + * @param backGroundPaint will fill background of image with this. May be null. + * @param fillStrategy strategy to use regarding how node will cover the + * image + * @return a rendering of this image and its descendants onto the specified + * image + */ + public Image toImage(final BufferedImage image, final Paint backGroundPaint, final int fillStrategy) { + final int imageWidth = image.getWidth(); + final int imageHeight = image.getHeight(); + final Graphics2D g2 = image.createGraphics(); + + if (backGroundPaint != null) { + g2.setPaint(backGroundPaint); + g2.fillRect(0, 0, imageWidth, imageHeight); + } + g2.setClip(0, 0, imageWidth, imageHeight); + + final PBounds nodeBounds = getFullBounds(); + nodeBounds.expandNearestIntegerDimensions(); + + final double nodeWidth = nodeBounds.getWidth(); + final double nodeHeight = nodeBounds.getHeight(); + + double imageRatio = imageWidth / (imageHeight * 1.0); + double nodeRatio = nodeWidth / nodeHeight; + double scale; + switch (fillStrategy) { + case FILL_STRATEGY_ASPECT_FIT: + // scale the graphics so node's full bounds fit in the imageable + // bounds but aspect ration is retained + + if (nodeRatio <= imageRatio) { + scale = image.getHeight() / nodeHeight; + } else { + scale = image.getWidth() / nodeWidth; + } + g2.scale(scale, scale); + g2.translate(-nodeBounds.x, -nodeBounds.y); + break; + case FILL_STRATEGY_ASPECT_COVER: + // scale the graphics so node completely covers the imageable + // area, but retains its aspect ratio. + if (nodeRatio <= imageRatio) { + scale = image.getWidth() / nodeWidth; + } else { + scale = image.getHeight() / nodeHeight; + } + g2.scale(scale, scale); + break; + case FILL_STRATEGY_EXACT_FIT: + // scale the node so that it covers then entire image, + // distorting it if necessary. + g2.scale(image.getWidth() / nodeWidth, image.getHeight() / nodeHeight); + g2.translate(-nodeBounds.x, -nodeBounds.y); + break; + default: + throw new IllegalArgumentException("Fill strategy provided is invalid"); + } + + final PPaintContext pc = new PPaintContext(g2); + pc.setRenderQuality(PPaintContext.HIGH_QUALITY_RENDERING); + fullPaint(pc); + return image; + } + + /** + * Constructs a new PrinterJob, allows the user to select which printer to print + * to, And then prints the node. + * + * @throws PrinterException if print fails + */ + public void print() throws PrinterException { + final PrinterJob printJob = PrinterJob.getPrinterJob(); + final PageFormat pageFormat = printJob.defaultPage(); + final Book book = new Book(); + book.append(this, pageFormat); + printJob.setPageable(book); + + if (printJob.printDialog()) { + printJob.print(); + } + } + + /** + * Prints the node into the given Graphics context using the specified format. + * The zero based index of the requested page is specified by pageIndex. If the + * requested page does not exist then this method returns NO_SUCH_PAGE; + * otherwise PAGE_EXISTS is returned. If the printable object aborts the print + * job then it throws a PrinterException. + * + * @param graphics the context into which the node is drawn + * @param pageFormat the size and orientation of the page + * @param pageIndex the zero based index of the page to be drawn + * + * @return Either NO_SUCH_PAGE or PAGE_EXISTS + */ + public int print(final Graphics graphics, final PageFormat pageFormat, final int pageIndex) { + if (pageIndex != 0) { + return NO_SUCH_PAGE; + } + + if (!(graphics instanceof Graphics2D)) { + throw new IllegalArgumentException("Provided graphics context is not a Graphics2D object"); + } + + final Graphics2D g2 = (Graphics2D) graphics; + final PBounds imageBounds = getFullBounds(); + + imageBounds.expandNearestIntegerDimensions(); + + g2.setClip(0, 0, (int) pageFormat.getWidth(), (int) pageFormat.getHeight()); + g2.translate(pageFormat.getImageableX(), pageFormat.getImageableY()); + + // scale the graphics so node's full bounds fit in the imageable bounds. + double scale = pageFormat.getImageableWidth() / imageBounds.getWidth(); + if (pageFormat.getImageableHeight() / imageBounds.getHeight() < scale) { + scale = pageFormat.getImageableHeight() / imageBounds.getHeight(); + } + + g2.scale(scale, scale); + g2.translate(-imageBounds.x, -imageBounds.y); + + final PPaintContext pc = new PPaintContext(g2); + pc.setRenderQuality(PPaintContext.HIGH_QUALITY_RENDERING); + fullPaint(pc); + + return PAGE_EXISTS; + } + + // **************************************************************** + // Picking - Methods for picking this node and its children. + // + // Picking is used to determine the node that intersects a point or + // rectangle on the screen. It is most frequently used by the + // PInputManager to determine the node that the cursor is over. + // + // The intersects() method is used to determine if a node has + // been picked or not. The default implementation just test to see + // if the pick bounds intersects the bounds of the node. Subclasses + // whose geometry (a circle for example) does not match up exactly with + // the bounds should override the intersects() method. + // + // The default picking behavior is to first try to pick the nodes + // children, and then try to pick the nodes own bounds. If a node + // wants specialized picking behavior it can override: + // + // pick() - Pick nodes here that should be picked before the nodes + // children are picked. + // pickAfterChildren() - Pick nodes here that should be picked after the + // node's children are picked. + // + // Note that fullPick should not normally be overridden. + // + // The pickable and childrenPickable flags can be used to make a + // node or it children not pickable even if their geometry does + // intersect the pick bounds. + // **************************************************************** + + /** + * Return true if this node is pickable. Only pickable nodes can receive input + * events. Nodes are pickable by default. + * + * @return true if this node is pickable + */ + public boolean getPickable() { + return pickable; + } + + /** + * Set the pickable flag for this node. Only pickable nodes can receive input + * events. Nodes are pickable by default. + * + * @param isPickable true if this node is pickable + */ + public void setPickable(final boolean isPickable) { + if (getPickable() != isPickable) { + pickable = isPickable; + firePropertyChange(PROPERTY_CODE_PICKABLE, PROPERTY_PICKABLE, null, null); + } + } + + /** + * Return true if the children of this node should be picked. If this flag is + * false then this node will not try to pick its children. Children are pickable + * by default. + * + * @return true if this node tries to pick its children + */ + public boolean getChildrenPickable() { + return childrenPickable; + } + + /** + * Set the children pickable flag. If this flag is false then this node will not + * try to pick its children. Children are pickable by default. + * + * @param areChildrenPickable true if this node tries to pick its children + */ + public void setChildrenPickable(final boolean areChildrenPickable) { + if (getChildrenPickable() != areChildrenPickable) { + childrenPickable = areChildrenPickable; + firePropertyChange(PROPERTY_CODE_CHILDREN_PICKABLE, PROPERTY_CHILDREN_PICKABLE, null, null); + } + } + + /** + * Try to pick this node before its children have had a chance to be picked. + * Nodes that paint on top of their children may want to override this method to + * if the pick path intersects that paint. + * + * @param pickPath the pick path used for the pick operation + * @return true if this node was picked + */ + protected boolean pick(final PPickPath pickPath) { + return false; + } + + /** + * Try to pick this node and all of its descendants. Most subclasses should not + * need to override this method. Instead they should override pick + * or pickAfterChildren. + * + * @param pickPath the pick path to add the node to if its picked + * @return true if this node or one of its descendants was picked. + */ + public boolean fullPick(final PPickPath pickPath) { + if (getVisible() && (getPickable() || getChildrenPickable()) && fullIntersects(pickPath.getPickBounds())) { + pickPath.pushNode(this); + pickPath.pushTransform(transform); + + final boolean thisPickable = getPickable() && pickPath.acceptsNode(this); + + if (thisPickable && pick(pickPath)) { + return true; + } + + if (getChildrenPickable()) { + final int count = getChildrenCount(); + for (int i = count - 1; i >= 0; i--) { + final PNode each = (PNode) children.get(i); + if (each.fullPick(pickPath)) { + return true; + } + } + } + + if (thisPickable && pickAfterChildren(pickPath)) { + return true; + } + + pickPath.popTransform(transform); + pickPath.popNode(this); + } + + return false; + } + + /** + * Finds all descendants of this node that intersect with the given bounds and + * adds them to the results array. + * + * @param fullBounds bounds to compare against + * @param results array into which to add matches + */ + public void findIntersectingNodes(final Rectangle2D fullBounds, final ArrayList results) { + if (fullIntersects(fullBounds)) { + final Rectangle2D localBounds = parentToLocal((Rectangle2D) fullBounds.clone()); + + if (intersects(localBounds)) { + results.add(this); + } + + final int count = getChildrenCount(); + for (int i = count - 1; i >= 0; i--) { + final PNode each = (PNode) children.get(i); + each.findIntersectingNodes(localBounds, results); + } + } + } + + /** + * Try to pick this node after its children have had a chance to be picked. Most + * subclasses the define a different geometry will need to override this method. + * + * @param pickPath the pick path used for the pick operation + * @return true if this node was picked + */ + protected boolean pickAfterChildren(final PPickPath pickPath) { + if (intersects(pickPath.getPickBounds())) { + return true; + } + return false; + } + + // **************************************************************** + // Structure - Methods for manipulating and traversing the + // parent child relationship + // + // Most of these methods won't need to be overridden by subclasses + // but you will use them frequently to build up your node structures. + // **************************************************************** + + /** + * Add a node to be a new child of this node. The new node is added to the end + * of the list of this node's children. If child was previously a child of + * another node, it is removed from that first. + * + * @param child the new child to add to this node + */ + public void addChild(final PNode child) { + int insertIndex = getChildrenCount(); + if (child.parent == this) { + insertIndex--; + } + addChild(insertIndex, child); + } + + /** + * Add a node to be a new child of this node at the specified index. If child + * was previously a child of another node, it is removed from that node first. + * + * @param index where in the children list to insert the child + * @param child the new child to add to this node + */ + public void addChild(final int index, final PNode child) { + final PNode oldParent = child.getParent(); + + if (oldParent != null) { + oldParent.removeChild(child); + } + + child.setParent(this); + getChildrenReference().add(index, child); + child.invalidatePaint(); + invalidateFullBounds(); + + firePropertyChange(PROPERTY_CODE_CHILDREN, PROPERTY_CHILDREN, null, children); + } + + /** + * Add a collection of nodes to be children of this node. If these nodes already + * have parents they will first be removed from those parents. + * + * @param nodes a collection of nodes to be added to this node + */ + public void addChildren(final Collection nodes) { + final Iterator i = nodes.iterator(); + while (i.hasNext()) { + final PNode each = (PNode) i.next(); + addChild(each); + } + } + + /** + * Return true if this node is an ancestor of the parameter node. + * + * @param node a possible descendant node + * @return true if this node is an ancestor of the given node + */ + public boolean isAncestorOf(final PNode node) { + PNode p = node.parent; + while (p != null) { + if (p == this) { + return true; + } + p = p.parent; + } + return false; + } + + /** + * Return true if this node is a descendant of the parameter node. + * + * @param node a possible ancestor node + * @return true if this nodes descends from the given node + */ + public boolean isDescendentOf(final PNode node) { + PNode p = parent; + while (p != null) { + if (p == node) { + return true; + } + p = p.parent; + } + return false; + } + + /** + * Return true if this node descends from the root. + * + * @return whether this node descends from root node + */ + public boolean isDescendentOfRoot() { + return getRoot() != null; + } + + /** + * Raise this node within the Z-order of its parent. + * + * @since 3.0 + */ + public void raise() { + final PNode p = parent; + if (p != null) { + final int index = parent.indexOfChild(this); + final int siblingIndex = Math.min(parent.getChildrenCount() - 1, index + 1); + if (siblingIndex != index) { + raiseAbove(parent.getChild(siblingIndex)); + } + } + } + + /** + * Lower this node within the Z-order of its parent. + * + * @since 3.0 + */ + public void lower() { + final PNode p = parent; + if (p != null) { + final int index = parent.indexOfChild(this); + final int siblingIndex = Math.max(0, index - 1); + if (siblingIndex != index) { + lowerBelow(parent.getChild(siblingIndex)); + } + } + } + + /** + * Raise this node within the Z-order of its parent to the top. + * + * @since 3.0 + */ + public void raiseToTop() { + final PNode p = parent; + if (p != null) { + p.removeChild(this); + p.addChild(this); + } + } + + /** + * Lower this node within the Z-order of its parent to the bottom. + * + * @since 3.0 + */ + public void lowerToBottom() { + final PNode p = parent; + if (p != null) { + p.removeChild(this); + p.addChild(0, this); + } + } + + /** + * Raise this node within the Z-order of its parent above the specified sibling + * node. + * + * @since 3.0 + * @param sibling sibling node to raise this node above + */ + public void raiseAbove(final PNode sibling) { + final PNode p = parent; + if (p != null && p == sibling.getParent()) { + p.removeChild(this); + final int index = p.indexOfChild(sibling); + p.addChild(index + 1, this); + } + } + + /** + * Lower this node within the Z-order of its parent below the specified sibling + * node. + * + * @since 3.0 + * @param sibling sibling node to lower this node below + */ + public void lowerBelow(final PNode sibling) { + final PNode p = parent; + if (p != null && p == sibling.getParent()) { + p.removeChild(this); + final int index = p.indexOfChild(sibling); + p.addChild(index, this); + } + } + + /** + * Raise the specified child node within the Z-order of this. + * + * @since 3.0 + * @param child child node to raise + */ + public void raise(final PNode child) { + if (children != null && children.contains(child) && this.equals(child.getParent())) { + child.raise(); + } + } + + /** + * Lower the specified child node within the Z-order of this. + * + * @since 3.0 + * @param child child node to lower + */ + public void lower(final PNode child) { + if (children != null && children.contains(child) && this.equals(child.getParent())) { + child.lower(); + } + } + + /** + * Raise the specified child node within the Z-order of this to the top. + * + * @since 3.0 + * @param child child node to raise to the top + */ + public void raiseToTop(final PNode child) { + if (children != null && children.contains(child) && this.equals(child.getParent())) { + child.raiseToTop(); + } + } + + /** + * Lower the specified child node within the Z-order of this to the bottom. + * + * @since 3.0 + * @param child child node to lower to the bottom + */ + public void lowerToBottom(final PNode child) { + if (children != null && children.contains(child) && this.equals(child.getParent())) { + child.lowerToBottom(); + } + } + + /** + * Return the parent of this node. This will be null if this node has not been + * added to a parent yet. + * + * @return this nodes parent or null + */ + public PNode getParent() { + return parent; + } + + /** + * Set the parent of this node. Note this is set automatically when adding and + * removing children. + * + * @param newParent the parent to which this node should be added + */ + public void setParent(final PNode newParent) { + final PNode old = parent; + parent = newParent; + firePropertyChange(PROPERTY_CODE_PARENT, PROPERTY_PARENT, old, parent); + } + + /** + * Return the index where the given child is stored. + * + * @param child child so search for + * @return index of child or -1 if not found + */ + public int indexOfChild(final PNode child) { + if (children == null) { + return -1; + } + return children.indexOf(child); + } + + /** + * Remove the given child from this node's children list. Any subsequent + * children are shifted to the left (one is subtracted from their indices). The + * removed child's parent is set to null. + * + * @param child the child to remove + * @return the removed child + */ + public PNode removeChild(final PNode child) { + final int index = indexOfChild(child); + if (index == -1) { + return null; + } + return removeChild(index); + } + + /** + * Remove the child at the specified position of this group node's children. Any + * subsequent children are shifted to the left (one is subtracted from their + * indices). The removed child's parent is set to null. + * + * @param index the index of the child to remove + * @return the removed child + */ + public PNode removeChild(final int index) { + if (children == null) { + return null; + } + final PNode child = (PNode) children.remove(index); + + if (children.size() == 0) { + children = null; + } + + child.repaint(); + child.setParent(null); + invalidateFullBounds(); + + firePropertyChange(PROPERTY_CODE_CHILDREN, PROPERTY_CHILDREN, null, children); + + return child; + } + + /** + * Remove all the children in the given collection from this node's list of + * children. All removed nodes will have their parent set to null. + * + * @param childrenNodes the collection of children to remove + */ + public void removeChildren(final Collection childrenNodes) { + final Iterator i = childrenNodes.iterator(); + while (i.hasNext()) { + final PNode each = (PNode) i.next(); + removeChild(each); + } + } + + /** + * Remove all the children from this node. Node this method is more efficient + * then removing each child individually. + */ + public void removeAllChildren() { + if (children != null) { + final int count = children.size(); + for (int i = 0; i < count; i++) { + final PNode each = (PNode) children.get(i); + each.setParent(null); + } + children = null; + invalidatePaint(); + invalidateFullBounds(); + + firePropertyChange(PROPERTY_CODE_CHILDREN, PROPERTY_CHILDREN, null, children); + } + } + + /** + * Delete this node by removing it from its parent's list of children. + */ + public void removeFromParent() { + if (parent != null) { + parent.removeChild(this); + } + } + + /** + * Set the parent of this node, and transform the node in such a way that it + * doesn't move in global coordinates. + * + * @param newParent The new parent of this node. + */ + public void reparent(final PNode newParent) { + final AffineTransform originalTransform = getLocalToGlobalTransform(null); + final AffineTransform newTransform = newParent.getGlobalToLocalTransform(null); + newTransform.concatenate(originalTransform); + + removeFromParent(); + setTransform(newTransform); + newParent.addChild(this); + computeFullBounds(fullBoundsCache); + } + + /** + * Swaps this node out of the scene graph tree, and replaces it with the + * specified replacement node. This node is left dangling, and it is up to the + * caller to manage it. The replacement node will be added to this node's parent + * in the same position as this was. That is, if this was the 3rd child of its + * parent, then after calling replaceWith(), the replacement node will also be + * the 3rd child of its parent. If this node has no parent when replace is + * called, then nothing will be done at all. + * + * @param replacementNode the new node that replaces the current node in the + * scene graph tree. + */ + public void replaceWith(final PNode replacementNode) { + if (parent != null) { + final PNode p = parent; + final int index = p.getChildrenReference().indexOf(this); + p.removeChild(this); + p.addChild(index, replacementNode); + } + } + + /** + * Sets the name of this node, may be null. + * + * @since 1.3 + * @param name new name for this node + */ + public void setName(final String name) { + this.name = name; + } + + /** + * Returns the name given to this node. + * + * @since 1.3 + * @return name given to this node, may be null + */ + public String getName() { + return name; + } + + /** + * Return the number of children that this node has. + * + * @return the number of children + */ + public int getChildrenCount() { + if (children == null) { + return 0; + } + return children.size(); + } + + /** + * Return the child node at the specified index. + * + * @param index a child index + * @return the child node at the specified index + */ + public PNode getChild(final int index) { + return (PNode) children.get(index); + } + + /** + * Return a reference to the list used to manage this node's children. This list + * should not be modified. + * + * @return reference to the children list + */ + public List getChildrenReference() { + if (children == null) { + children = new ArrayList(); + } + return children; + } + + /** + * Return an iterator over this node's direct descendant children. + * + * @return iterator over this nodes children + */ + public ListIterator getChildrenIterator() { + if (children == null) { + List list = Collections.emptyList(); + return list.listIterator(); + } + return Collections.unmodifiableList(children).listIterator(); + } + + /** + * Return the root node (instance of PRoot). If this node does not descend from + * a PRoot then null will be returned. + * + * @return root element of this node, or null if this node does not descend from + * a PRoot + */ + public PRoot getRoot() { + if (parent != null) { + return parent.getRoot(); + } + return null; + } + + /** + * Return a collection containing this node and all of its descendant nodes. + * + * @return a new collection containing this node and all descendants + */ + public Collection getAllNodes() { + return getAllNodes(null, null); + } + + /** + * Return a collection containing the subset of this node and all of its + * descendant nodes that are accepted by the given node filter. If the filter is + * null then all nodes will be accepted. If the results parameter is not null + * then it will be used to collect this subset instead of creating a new + * collection. + * + * @param filter the filter used to determine the subset + * @param resultantNodes where matching nodes should be added + * @return a collection containing this node and all descendants + */ + public Collection getAllNodes(final PNodeFilter filter, final Collection resultantNodes) { + Collection results; + if (resultantNodes == null) { + results = new ArrayList(); + } else { + results = resultantNodes; + } + + if (filter == null || filter.accept(this)) { + results.add(this); + } + + if (filter == null || filter.acceptChildrenOf(this)) { + final int count = getChildrenCount(); + for (int i = 0; i < count; i++) { + final PNode each = (PNode) children.get(i); + each.getAllNodes(filter, results); + } + } + + return results; + } + + // **************************************************************** + // Serialization - Nodes conditionally serialize their parent. + // This means that only the parents that were unconditionally + // (using writeObject) serialized by someone else will be restored + // when the node is unserialized. + // **************************************************************** + + /** + * Write this node and all of its descendant nodes to the given outputsteam. + * This stream must be an instance of PObjectOutputStream or serialization will + * fail. This nodes parent is written out conditionally, that is it will only be + * written out if someone else writes it out unconditionally. + * + * @param out the output stream to write to, must be an instance of + * PObjectOutputStream + * @throws IOException when an error occurs speaking to underlying + * ObjectOutputStream + */ + private void writeObject(final ObjectOutputStream out) throws IOException { + if (!(out instanceof PObjectOutputStream)) { + throw new IllegalArgumentException("PNode.writeObject may only be used with PObjectOutputStreams"); + } + out.defaultWriteObject(); + ((PObjectOutputStream) out).writeConditionalObject(parent); + } + + /** + * Read this node and all of its descendants in from the given input stream. + * + * @param in the stream to read from + * + * @throws IOException when an error occurs speaking to underlying + * ObjectOutputStream + * @throws ClassNotFoundException when a class is deserialized that no longer + * exists. This can happen if it's renamed or + * deleted. + */ + private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { + in.defaultReadObject(); + parent = (PNode) in.readObject(); + } + + /** + * Returns an array of input event listeners that are attached to this node. + * + * @since 1.3 + * @return event listeners attached to this node + */ + public PInputEventListener[] getInputEventListeners() { + if (listenerList == null || listenerList.getListenerCount() == 0) { + return new PInputEventListener[] {}; + } + + final EventListener[] listeners = listenerList.getListeners(PInputEventListener.class); + + final PInputEventListener[] result = new PInputEventListener[listeners.length]; + for (int i = 0; i < listeners.length; i++) { + result[i] = (PInputEventListener) listeners[i]; + } + return result; + } + + /** + * PSceneGraphDelegate is an interface to receive low level node events. + * It together with PNode.SCENE_GRAPH_DELEGATE gives Piccolo2d users an + * efficient way to learn about low level changes in Piccolo's scene graph. Most + * users will not need to use this. + */ + public interface PSceneGraphDelegate { + /** + * Called to notify delegate that the node needs repainting. + * + * @param node node needing repaint + */ + void nodePaintInvalidated(PNode node); + + /** + * Called to notify delegate that the node and all it's children need + * repainting. + * + * @param node node needing repaint + */ + void nodeFullBoundsInvalidated(PNode node); + } } diff --git a/core/src/main/java/org/piccolo2d/POffscreenCanvas.java b/core/src/main/java/org/piccolo2d/POffscreenCanvas.java index bed1b516..37b5cca1 100644 --- a/core/src/main/java/org/piccolo2d/POffscreenCanvas.java +++ b/core/src/main/java/org/piccolo2d/POffscreenCanvas.java @@ -36,7 +36,6 @@ import org.piccolo2d.util.PPaintContext; import org.piccolo2d.util.PUtil; - /** * Offscreen canvas. * @@ -44,193 +43,194 @@ */ public final class POffscreenCanvas implements PComponent { - /** Default render quality, PPaintContext.HIGH_QUALITY_RENDERING. */ - static final int DEFAULT_RENDER_QUALITY = PPaintContext.HIGH_QUALITY_RENDERING; - - /** Bounds of this offscreen canvas. */ - private final PBounds bounds; - - /** Camera for this offscreen canvas. */ - private PCamera camera; - - /** Render quality. */ - private int renderQuality = DEFAULT_RENDER_QUALITY; - - /** True if this offscreen canvas is opaque. */ - private boolean opaque; - - /** Background color for this offscreen canvas. */ - private Color backgroundColor; - - - /** - * Create a new offscreen canvas the specified width and height. - * - * @param width width of this offscreen canvas, must be at least zero - * @param height height of this offscreen canvas, must be at least zero - */ - public POffscreenCanvas(final int width, final int height) { - if (width < 0) { - throw new IllegalArgumentException("width must be at least zero, was " + width); - } - if (height < 0) { - throw new IllegalArgumentException("height must be at least zero, was " + height); - } - bounds = new PBounds(0.0d, 0.0d, width, height); - setCamera(PUtil.createBasicScenegraph()); - - opaque = false; - backgroundColor = null; - } - - - /** - * Render this offscreen canvas to the specified graphics. - * - * @param graphics graphics to render this offscreen canvas to, must not be null - */ - public void render(final Graphics2D graphics) { - if (graphics == null) { - throw new IllegalArgumentException("graphics must not be null"); - } - - if (opaque && backgroundColor != null) { - graphics.setBackground(backgroundColor); - graphics.clearRect(0, 0, (int) bounds.getWidth(), (int) bounds.getHeight()); - } - - final PPaintContext paintContext = new PPaintContext(graphics); - paintContext.setRenderQuality(renderQuality); - camera.fullPaint(paintContext); - } - - /** - * Set the camera for this offscreen canvas to camera. - * - * @param camera camera for this offscreen canvas - */ - public void setCamera(final PCamera camera) { - if (this.camera != null) { - this.camera.setComponent(null); - } - this.camera = camera; - if (camera != null) { - camera.setComponent(this); - camera.setBounds((PBounds) bounds.clone()); - } - } - - /** - * Return the camera for this offscreen canvas. - * - * @return the camera for this offscreen canvas - */ - public PCamera getCamera() { - return camera; - } - - /** - * Set the render quality hint for this offscreen canvas to - * renderQuality. - * - * @param renderQuality render quality hint, must be one of - * PPaintContext.HIGH_QUALITY_RENDERING or - * PPaintContext.LOW_QUALITY_RENDERING - */ - public void setRenderQuality(final int renderQuality) { - if (renderQuality == PPaintContext.HIGH_QUALITY_RENDERING - || renderQuality == PPaintContext.LOW_QUALITY_RENDERING) { - this.renderQuality = renderQuality; - } - else { - throw new IllegalArgumentException("renderQuality must be one of PPaintContext.HIGH_QUALITY_RENDERING" - + " or PPaintContext.LOW_QUALITY_RENDERING, was " + renderQuality); - } - } - - /** - * Return the render quality hint for this offscreen canvas. - * - * @return the render quality hint for this offscreen canvas - */ - public int getRenderQuality() { - return renderQuality; - } - - /** {@inheritDoc} */ - public void paintImmediately() { - // empty - } - - /** {@inheritDoc} */ - public void popCursor() { - // empty - } - - /** {@inheritDoc} */ - public void pushCursor(final Cursor cursor) { - // empty - } - - /** {@inheritDoc} */ - public void repaint(final PBounds repaintBounds) { - // empty - } - - /** {@inheritDoc} */ - public void setInteracting(final boolean interacting) { - // empty - } - - /** - * Return the root node of the scene graph for this offscreen canvas. The - * root node will be null if the camera for this offscreen canvas is null. - * - * @return the root node of the scene graph for this offscreen canvas - */ - public PRoot getRoot() { - return camera == null ? null : camera.getRoot(); - } - - /** - * Return true if this offscreen canvas is opaque. Defaults to false. - * - * @return true if this offscreen canvas is opaque - */ - public boolean isOpaque() { - return opaque; - } - - /** - * Set to true if this offscreen canvas is opaque. - * - * @param opaque true if this offscreen canvas is opaque - */ - public void setOpaque(final boolean opaque) { - this.opaque = opaque; - } - - /** - * Return the background color for this offscreen canvas. If this - * offscreen canvas is opaque, the background color will be painted - * before the contents of the scene are rendered. - * - * @see #isOpaque - * @return the background color for this offscreen canvas - */ - public Color getBackground() { - return backgroundColor; - } - - /** - * Set the background color for this offscreen canvas to backgroundColor. - * If this offscreen canvas is opaque, the background color will be painted - * before the contents of the scene are rendered. - * - * @see #isOpaque - * @param backgroundColor background color for this offscreen canvas - */ - public void setBackground(final Color backgroundColor) { - this.backgroundColor = backgroundColor; - } + /** + * Default render quality, PPaintContext.HIGH_QUALITY_RENDERING. + */ + static final int DEFAULT_RENDER_QUALITY = PPaintContext.HIGH_QUALITY_RENDERING; + + /** Bounds of this offscreen canvas. */ + private final PBounds bounds; + + /** Camera for this offscreen canvas. */ + private PCamera camera; + + /** Render quality. */ + private int renderQuality = DEFAULT_RENDER_QUALITY; + + /** True if this offscreen canvas is opaque. */ + private boolean opaque; + + /** Background color for this offscreen canvas. */ + private Color backgroundColor; + + /** + * Create a new offscreen canvas the specified width and height. + * + * @param width width of this offscreen canvas, must be at least zero + * @param height height of this offscreen canvas, must be at least zero + */ + public POffscreenCanvas(final int width, final int height) { + if (width < 0) { + throw new IllegalArgumentException("width must be at least zero, was " + width); + } + if (height < 0) { + throw new IllegalArgumentException("height must be at least zero, was " + height); + } + bounds = new PBounds(0.0d, 0.0d, width, height); + setCamera(PUtil.createBasicScenegraph()); + + opaque = false; + backgroundColor = null; + } + + /** + * Render this offscreen canvas to the specified graphics. + * + * @param graphics graphics to render this offscreen canvas to, must not be null + */ + public void render(final Graphics2D graphics) { + if (graphics == null) { + throw new IllegalArgumentException("graphics must not be null"); + } + + if (opaque && backgroundColor != null) { + graphics.setBackground(backgroundColor); + graphics.clearRect(0, 0, (int) bounds.getWidth(), (int) bounds.getHeight()); + } + + final PPaintContext paintContext = new PPaintContext(graphics); + paintContext.setRenderQuality(renderQuality); + camera.fullPaint(paintContext); + } + + /** + * Set the camera for this offscreen canvas to camera. + * + * @param camera camera for this offscreen canvas + */ + public void setCamera(final PCamera camera) { + if (this.camera != null) { + this.camera.setComponent(null); + } + this.camera = camera; + if (camera != null) { + camera.setComponent(this); + camera.setBounds((PBounds) bounds.clone()); + } + } + + /** + * Return the camera for this offscreen canvas. + * + * @return the camera for this offscreen canvas + */ + public PCamera getCamera() { + return camera; + } + + /** + * Set the render quality hint for this offscreen canvas to + * renderQuality. + * + * @param renderQuality render quality hint, must be one of + * PPaintContext.HIGH_QUALITY_RENDERING or + * PPaintContext.LOW_QUALITY_RENDERING + */ + public void setRenderQuality(final int renderQuality) { + if (renderQuality == PPaintContext.HIGH_QUALITY_RENDERING + || renderQuality == PPaintContext.LOW_QUALITY_RENDERING) { + this.renderQuality = renderQuality; + } else { + throw new IllegalArgumentException("renderQuality must be one of PPaintContext.HIGH_QUALITY_RENDERING" + + " or PPaintContext.LOW_QUALITY_RENDERING, was " + renderQuality); + } + } + + /** + * Return the render quality hint for this offscreen canvas. + * + * @return the render quality hint for this offscreen canvas + */ + public int getRenderQuality() { + return renderQuality; + } + + /** {@inheritDoc} */ + public void paintImmediately() { + // empty + } + + /** {@inheritDoc} */ + public void popCursor() { + // empty + } + + /** {@inheritDoc} */ + public void pushCursor(final Cursor cursor) { + // empty + } + + /** {@inheritDoc} */ + public void repaint(final PBounds repaintBounds) { + // empty + } + + /** {@inheritDoc} */ + public void setInteracting(final boolean interacting) { + // empty + } + + /** + * Return the root node of the scene graph for this offscreen canvas. The root + * node will be null if the camera for this offscreen canvas is null. + * + * @return the root node of the scene graph for this offscreen canvas + */ + public PRoot getRoot() { + return camera == null ? null : camera.getRoot(); + } + + /** + * Return true if this offscreen canvas is opaque. Defaults to + * false. + * + * @return true if this offscreen canvas is opaque + */ + public boolean isOpaque() { + return opaque; + } + + /** + * Set to true if this offscreen canvas is opaque. + * + * @param opaque true if this offscreen canvas is opaque + */ + public void setOpaque(final boolean opaque) { + this.opaque = opaque; + } + + /** + * Return the background color for this offscreen canvas. If this offscreen + * canvas is opaque, the background color will be painted before the contents of + * the scene are rendered. + * + * @see #isOpaque + * @return the background color for this offscreen canvas + */ + public Color getBackground() { + return backgroundColor; + } + + /** + * Set the background color for this offscreen canvas to + * backgroundColor. If this offscreen canvas is opaque, the + * background color will be painted before the contents of the scene are + * rendered. + * + * @see #isOpaque + * @param backgroundColor background color for this offscreen canvas + */ + public void setBackground(final Color backgroundColor) { + this.backgroundColor = backgroundColor; + } } diff --git a/core/src/main/java/org/piccolo2d/PRoot.java b/core/src/main/java/org/piccolo2d/PRoot.java index e16d3d75..2bd5a92c 100644 --- a/core/src/main/java/org/piccolo2d/PRoot.java +++ b/core/src/main/java/org/piccolo2d/PRoot.java @@ -41,7 +41,6 @@ import org.piccolo2d.util.PDebug; import org.piccolo2d.util.PNodeFilter; - /** * PRoot serves as the top node in Piccolo2D's runtime structure. The * PRoot responsible for running the main UI loop that processes input from @@ -53,366 +52,362 @@ */ public class PRoot extends PNode { - /** - * Allows for future serialization code to understand versioned binary - * formats. - */ - private static final long serialVersionUID = 1L; - - /** - * The property name that identifies a change in the set of this root's - * input sources (see {@link InputSource InputSource}). In any property - * change event the new value will be a reference to the list of this root's - * input sources, but old value will always be null. - */ - public static final String PROPERTY_INPUT_SOURCES = "inputSources"; - - /** - * The property code that identifies a change in the set of this root's - * input sources (see {@link InputSource InputSource}). In any property - * change event the new value will be a reference to the list of this root's - * input sources, but old value will always be null. - */ - public static final int PROPERTY_CODE_INPUT_SOURCES = 1 << 14; - - /** - * The property name that identifies a change in this node's interacting - * state. - * - * @since 1.3 - */ - public static final String PROPERTY_INTERACTING_CHANGED = "INTERACTING_CHANGED_NOTIFICATION"; - - /** - * The property code that identifies a change in this node's interacting - * state. - * - * @since 1.3 - */ - public static final int PROPERTY_CODE_INTERACTING_CHANGED = 1 << 13; - - /** Whether this not is currently processing inputs. */ - protected transient boolean processingInputs; - - /** Whether this node needs to have its inputs processed. */ - protected transient boolean processInputsScheduled; - - /** The number of interactions this node is currently participating in. */ - private transient int interacting; - - /** - * The singleton instance of the default input manager. - */ - private transient PInputManager defaultInputManager; - - /** The Input Sources that are registered to this node. */ - private final transient List inputSources; - - /** - * Used to provide a consistent clock time to activities as they are being - * processed. - * - * Should it happen that an activity step take longer than a millisecond, - * the next step will be unaffected by the change in clock had it used - * System.currentMillis(). - */ - private transient long globalTime; - - /** - * Object responsible for scheduling activities, regardless of where in the - * scene they take place. - */ - private final PActivityScheduler activityScheduler; - - /** - * Construct a new PRoot(). Note the PCanvas already creates a basic scene - * graph for you so often you will not need to construct your own roots. - */ - public PRoot() { - super(); - inputSources = new ArrayList(); - globalTime = System.currentTimeMillis(); - activityScheduler = new PActivityScheduler(this); - } - - // **************************************************************** - // Activities - // **************************************************************** - - /** - * Add an activity to the activity scheduler associated with this root. - * Activities are given a chance to run during each call to the roots - * processInputs method. When the activity has finished running - * it will automatically get removed. - * - * @param activity Activity that should be scheduled - * @return whether it has been scheduled (always true) - */ - public boolean addActivity(final PActivity activity) { - getActivityScheduler().addActivity(activity); - return true; - } - - /** - * Get the activity scheduler associated with this root. - * - * @return associated scheduler - */ - public PActivityScheduler getActivityScheduler() { - return activityScheduler; - } - - /** - * Wait for all scheduled activities to finish before returning from this - * method. This will freeze out user input, and so it is generally - * recommended that you use PActivities.setTriggerTime() to offset - * activities instead of using this method. - */ - public void waitForActivities() { - final PNodeFilter cameraWithCanvas = new CameraWithCanvasFilter(); - - while (activityScheduler.getActivitiesReference().size() > 0) { - processInputs(); - final Iterator i = getAllNodes(cameraWithCanvas, null).iterator(); - while (i.hasNext()) { - final PCamera each = (PCamera) i.next(); - each.getComponent().paintImmediately(); - } - } - } - - /** - * Since getRoot is handled recursively, and root is the lowest point in the - * hierarchy, simply returns itself. - * - * @return itself - */ - public PRoot getRoot() { - return this; - } - - /** - * Get the default input manager to be used when processing input events. - * PCanvas's use this method when they forward new swing input events to the - * PInputManager. - * - * @return a singleton instance of PInputManager - */ - public PInputManager getDefaultInputManager() { - if (defaultInputManager == null) { - defaultInputManager = new PInputManager(); - addInputSource(defaultInputManager); - } - return defaultInputManager; - } - - /** - * Return true if this root has been marked as interacting. If so the root - * will normally render at a lower quality that is faster. - * - * @since 1.3 - * @return true if this root has user interaction taking place - */ - public boolean getInteracting() { - return interacting > 0; - } - - /** - * Set if this root is interacting. If so the root will normally render at a - * lower quality that is faster. Also repaints the root if the the - * interaction has ended. - *

- * This has similar functionality to the setInteracting method on Canvas, - * but this is the appropriate place to mark interactions that may occur in - * multiple canvases if this Root is shared. - * - * @since 1.3 - * @param isInteracting True if this root has user interaction taking place - * @see PCanvas#setInteracting(boolean) - */ - public void setInteracting(final boolean isInteracting) { - final boolean wasInteracting = getInteracting(); - - if (isInteracting) { - interacting++; - } - else { - interacting--; - } - - if (!isInteracting && !getInteracting()) { - // force all the child cameras to repaint - for (int i = 0; i < getChildrenCount(); i++) { - final PNode child = getChild(i); - if (child instanceof PCamera) { - child.repaint(); - } - } - - } - if (wasInteracting != isInteracting) { - firePropertyChange(PROPERTY_CODE_INTERACTING_CHANGED, PROPERTY_INTERACTING_CHANGED, Boolean - .valueOf(wasInteracting), Boolean.valueOf(isInteracting)); - } - } - - /** - * Advanced. If you want to add additional input sources to the roots UI - * process you can do that here. You will seldom do this unless you are - * making additions to the Piccolo2D framework. - * - * @param inputSource An input source that should be added - */ - public void addInputSource(final InputSource inputSource) { - inputSources.add(inputSource); - firePropertyChange(PROPERTY_CODE_INPUT_SOURCES, PROPERTY_INPUT_SOURCES, null, inputSources); - } - - /** - * Advanced. If you want to remove the default input source from the roots - * UI process you can do that here. You will seldom do this unless you are - * making additions to the Piccolo2D framework. - * - * @param inputSource input source that should no longer be asked about - * input events - */ - public void removeInputSource(final InputSource inputSource) { - if (inputSources.remove(inputSource)) { - firePropertyChange(PROPERTY_CODE_INPUT_SOURCES, PROPERTY_INPUT_SOURCES, null, inputSources); - } - } - - /** - * Returns a new timer. This method allows subclasses, such as PSWTRoot to - * create custom timers that will be used transparently by the Piccolo2D - * framework. - * - * @param delay # of milliseconds before action listener is invoked - * @param listener listener to be invoked after delay - * - * @return A new Timer - */ - public Timer createTimer(final int delay, final ActionListener listener) { - return new Timer(delay, listener); - } - - // **************************************************************** - // UI Loop - Methods for running the main UI loop of Piccolo2D. - // **************************************************************** - - /** - * Get the global Piccolo2D time. This is set to System.currentTimeMillis() - * at the beginning of the roots processInputs method. - * Activities should usually use this global time instead of System. - * currentTimeMillis() so that multiple activities will be synchronized. - * - * @return time as recorded at the beginning of activity scheduling - */ - public long getGlobalTime() { - return globalTime; - } - - /** - * This is the heartbeat of the Piccolo2D framework. Pending input events - * are processed. Activities are given a chance to run, and the bounds - * caches and any paint damage is validated. - */ - public void processInputs() { - PDebug.startProcessingInput(); - processingInputs = true; - - globalTime = System.currentTimeMillis(); - if (inputSources.size() > 0) { - final Iterator inputSourceIterator = inputSources.iterator(); - while (inputSourceIterator.hasNext()) { - final InputSource each = (InputSource) inputSourceIterator.next(); - each.processInput(); - } - } - - activityScheduler.processActivities(globalTime); - validateFullBounds(); - validateFullPaint(); - - processingInputs = false; - PDebug.endProcessingInput(); - } - - /** {@inheritDoc} */ - public void setFullBoundsInvalid(final boolean fullLayoutInvalid) { - super.setFullBoundsInvalid(fullLayoutInvalid); - scheduleProcessInputsIfNeeded(); - } - - /** {@inheritDoc} */ - public void setChildBoundsInvalid(final boolean childLayoutInvalid) { - super.setChildBoundsInvalid(childLayoutInvalid); - scheduleProcessInputsIfNeeded(); - } - - /** {@inheritDoc} */ - public void setPaintInvalid(final boolean paintInvalid) { - super.setPaintInvalid(paintInvalid); - scheduleProcessInputsIfNeeded(); - } - - /** {@inheritDoc} */ - public void setChildPaintInvalid(final boolean childPaintInvalid) { - super.setChildPaintInvalid(childPaintInvalid); - scheduleProcessInputsIfNeeded(); - } - - /** - * Schedule process inputs if needed. - */ - public void scheduleProcessInputsIfNeeded() { - /* - * The reason for the special case here (when not in the event dispatch - * thread) is that the SwingUtilitiles.invokeLater code below only - * invokes later with respect to the event dispatch thread, it will - * invoke concurrently with other threads. - */ - if (!SwingUtilities.isEventDispatchThread()) { - /* - * Piccolo2D is not thread safe and should almost always be called - * from the Swing event dispatch thread. It should only reach this - * point when a new canvas is being created. - */ - return; - } - - PDebug.scheduleProcessInputs(); - - if (!processInputsScheduled && !processingInputs - && (getFullBoundsInvalid() || getChildBoundsInvalid() || getPaintInvalid() || getChildPaintInvalid())) { - - processInputsScheduled = true; - SwingUtilities.invokeLater(new Runnable() { - public void run() { - processInputs(); - processInputsScheduled = false; - } - }); - } - } - - private static final class CameraWithCanvasFilter implements PNodeFilter { - public boolean accept(final PNode aNode) { - return aNode instanceof PCamera && ((PCamera) aNode).getComponent() != null; - } - - public boolean acceptChildrenOf(final PNode aNode) { - return true; - } - } - - /** - * This interfaces is for advanced use only. If you want to implement a - * different kind of input framework then Piccolo2D provides you can hook it - * in here. - */ - public static interface InputSource { - /** Causes the system to process any pending Input Events. */ - void processInput(); - } + /** + * Allows for future serialization code to understand versioned binary formats. + */ + private static final long serialVersionUID = 1L; + + /** + * The property name that identifies a change in the set of this root's input + * sources (see {@link InputSource InputSource}). In any property change event + * the new value will be a reference to the list of this root's input sources, + * but old value will always be null. + */ + public static final String PROPERTY_INPUT_SOURCES = "inputSources"; + + /** + * The property code that identifies a change in the set of this root's input + * sources (see {@link InputSource InputSource}). In any property change event + * the new value will be a reference to the list of this root's input sources, + * but old value will always be null. + */ + public static final int PROPERTY_CODE_INPUT_SOURCES = 1 << 14; + + /** + * The property name that identifies a change in this node's interacting state. + * + * @since 1.3 + */ + public static final String PROPERTY_INTERACTING_CHANGED = "INTERACTING_CHANGED_NOTIFICATION"; + + /** + * The property code that identifies a change in this node's interacting state. + * + * @since 1.3 + */ + public static final int PROPERTY_CODE_INTERACTING_CHANGED = 1 << 13; + + /** Whether this not is currently processing inputs. */ + protected transient boolean processingInputs; + + /** Whether this node needs to have its inputs processed. */ + protected transient boolean processInputsScheduled; + + /** The number of interactions this node is currently participating in. */ + private transient int interacting; + + /** + * The singleton instance of the default input manager. + */ + private transient PInputManager defaultInputManager; + + /** The Input Sources that are registered to this node. */ + private final transient List inputSources; + + /** + * Used to provide a consistent clock time to activities as they are being + * processed. + * + * Should it happen that an activity step take longer than a millisecond, the + * next step will be unaffected by the change in clock had it used + * System.currentMillis(). + */ + private transient long globalTime; + + /** + * Object responsible for scheduling activities, regardless of where in the + * scene they take place. + */ + private final PActivityScheduler activityScheduler; + + /** + * Construct a new PRoot(). Note the PCanvas already creates a basic scene graph + * for you so often you will not need to construct your own roots. + */ + public PRoot() { + super(); + inputSources = new ArrayList(); + globalTime = System.currentTimeMillis(); + activityScheduler = new PActivityScheduler(this); + } + + // **************************************************************** + // Activities + // **************************************************************** + + /** + * Add an activity to the activity scheduler associated with this root. + * Activities are given a chance to run during each call to the roots + * processInputs method. When the activity has finished running it + * will automatically get removed. + * + * @param activity Activity that should be scheduled + * @return whether it has been scheduled (always true) + */ + public boolean addActivity(final PActivity activity) { + getActivityScheduler().addActivity(activity); + return true; + } + + /** + * Get the activity scheduler associated with this root. + * + * @return associated scheduler + */ + public PActivityScheduler getActivityScheduler() { + return activityScheduler; + } + + /** + * Wait for all scheduled activities to finish before returning from this + * method. This will freeze out user input, and so it is generally recommended + * that you use PActivities.setTriggerTime() to offset activities instead of + * using this method. + */ + public void waitForActivities() { + final PNodeFilter cameraWithCanvas = new CameraWithCanvasFilter(); + + while (activityScheduler.getActivitiesReference().size() > 0) { + processInputs(); + final Iterator i = getAllNodes(cameraWithCanvas, null).iterator(); + while (i.hasNext()) { + final PCamera each = (PCamera) i.next(); + each.getComponent().paintImmediately(); + } + } + } + + /** + * Since getRoot is handled recursively, and root is the lowest point in the + * hierarchy, simply returns itself. + * + * @return itself + */ + public PRoot getRoot() { + return this; + } + + /** + * Get the default input manager to be used when processing input events. + * PCanvas's use this method when they forward new swing input events to the + * PInputManager. + * + * @return a singleton instance of PInputManager + */ + public PInputManager getDefaultInputManager() { + if (defaultInputManager == null) { + defaultInputManager = new PInputManager(); + addInputSource(defaultInputManager); + } + return defaultInputManager; + } + + /** + * Return true if this root has been marked as interacting. If so the root will + * normally render at a lower quality that is faster. + * + * @since 1.3 + * @return true if this root has user interaction taking place + */ + public boolean getInteracting() { + return interacting > 0; + } + + /** + * Set if this root is interacting. If so the root will normally render at a + * lower quality that is faster. Also repaints the root if the the interaction + * has ended. + *

+ * This has similar functionality to the setInteracting method on Canvas, but + * this is the appropriate place to mark interactions that may occur in multiple + * canvases if this Root is shared. + * + * @since 1.3 + * @param isInteracting True if this root has user interaction taking place + * @see PCanvas#setInteracting(boolean) + */ + public void setInteracting(final boolean isInteracting) { + final boolean wasInteracting = getInteracting(); + + if (isInteracting) { + interacting++; + } else { + interacting--; + } + + if (!isInteracting && !getInteracting()) { + // force all the child cameras to repaint + for (int i = 0; i < getChildrenCount(); i++) { + final PNode child = getChild(i); + if (child instanceof PCamera) { + child.repaint(); + } + } + + } + if (wasInteracting != isInteracting) { + firePropertyChange(PROPERTY_CODE_INTERACTING_CHANGED, PROPERTY_INTERACTING_CHANGED, + Boolean.valueOf(wasInteracting), Boolean.valueOf(isInteracting)); + } + } + + /** + * Advanced. If you want to add additional input sources to the roots UI process + * you can do that here. You will seldom do this unless you are making additions + * to the Piccolo2D framework. + * + * @param inputSource An input source that should be added + */ + public void addInputSource(final InputSource inputSource) { + inputSources.add(inputSource); + firePropertyChange(PROPERTY_CODE_INPUT_SOURCES, PROPERTY_INPUT_SOURCES, null, inputSources); + } + + /** + * Advanced. If you want to remove the default input source from the roots UI + * process you can do that here. You will seldom do this unless you are making + * additions to the Piccolo2D framework. + * + * @param inputSource input source that should no longer be asked about input + * events + */ + public void removeInputSource(final InputSource inputSource) { + if (inputSources.remove(inputSource)) { + firePropertyChange(PROPERTY_CODE_INPUT_SOURCES, PROPERTY_INPUT_SOURCES, null, inputSources); + } + } + + /** + * Returns a new timer. This method allows subclasses, such as PSWTRoot to + * create custom timers that will be used transparently by the Piccolo2D + * framework. + * + * @param delay # of milliseconds before action listener is invoked + * @param listener listener to be invoked after delay + * + * @return A new Timer + */ + public Timer createTimer(final int delay, final ActionListener listener) { + return new Timer(delay, listener); + } + + // **************************************************************** + // UI Loop - Methods for running the main UI loop of Piccolo2D. + // **************************************************************** + + /** + * Get the global Piccolo2D time. This is set to System.currentTimeMillis() at + * the beginning of the roots processInputs method. Activities + * should usually use this global time instead of System. currentTimeMillis() so + * that multiple activities will be synchronized. + * + * @return time as recorded at the beginning of activity scheduling + */ + public long getGlobalTime() { + return globalTime; + } + + /** + * This is the heartbeat of the Piccolo2D framework. Pending input events are + * processed. Activities are given a chance to run, and the bounds caches and + * any paint damage is validated. + */ + public void processInputs() { + PDebug.startProcessingInput(); + processingInputs = true; + + globalTime = System.currentTimeMillis(); + if (inputSources.size() > 0) { + final Iterator inputSourceIterator = inputSources.iterator(); + while (inputSourceIterator.hasNext()) { + final InputSource each = inputSourceIterator.next(); + each.processInput(); + } + } + + activityScheduler.processActivities(globalTime); + validateFullBounds(); + validateFullPaint(); + + processingInputs = false; + PDebug.endProcessingInput(); + } + + /** {@inheritDoc} */ + public void setFullBoundsInvalid(final boolean fullLayoutInvalid) { + super.setFullBoundsInvalid(fullLayoutInvalid); + scheduleProcessInputsIfNeeded(); + } + + /** {@inheritDoc} */ + public void setChildBoundsInvalid(final boolean childLayoutInvalid) { + super.setChildBoundsInvalid(childLayoutInvalid); + scheduleProcessInputsIfNeeded(); + } + + /** {@inheritDoc} */ + public void setPaintInvalid(final boolean paintInvalid) { + super.setPaintInvalid(paintInvalid); + scheduleProcessInputsIfNeeded(); + } + + /** {@inheritDoc} */ + public void setChildPaintInvalid(final boolean childPaintInvalid) { + super.setChildPaintInvalid(childPaintInvalid); + scheduleProcessInputsIfNeeded(); + } + + /** + * Schedule process inputs if needed. + */ + public void scheduleProcessInputsIfNeeded() { + /* + * The reason for the special case here (when not in the event dispatch thread) + * is that the SwingUtilitiles.invokeLater code below only invokes later with + * respect to the event dispatch thread, it will invoke concurrently with other + * threads. + */ + if (!SwingUtilities.isEventDispatchThread()) { + /* + * Piccolo2D is not thread safe and should almost always be called from the + * Swing event dispatch thread. It should only reach this point when a new + * canvas is being created. + */ + return; + } + + PDebug.scheduleProcessInputs(); + + if (!processInputsScheduled && !processingInputs + && (getFullBoundsInvalid() || getChildBoundsInvalid() || getPaintInvalid() || getChildPaintInvalid())) { + + processInputsScheduled = true; + SwingUtilities.invokeLater(new Runnable() { + public void run() { + processInputs(); + processInputsScheduled = false; + } + }); + } + } + + private static final class CameraWithCanvasFilter implements PNodeFilter { + public boolean accept(final PNode aNode) { + return aNode instanceof PCamera && ((PCamera) aNode).getComponent() != null; + } + + public boolean acceptChildrenOf(final PNode aNode) { + return true; + } + } + + /** + * This interfaces is for advanced use only. If you want to implement a + * different kind of input framework then Piccolo2D provides you can hook it in + * here. + */ + public static interface InputSource { + /** Causes the system to process any pending Input Events. */ + void processInput(); + } } diff --git a/core/src/main/java/org/piccolo2d/activities/PActivityScheduler.java b/core/src/main/java/org/piccolo2d/activities/PActivityScheduler.java index 5b840801..dff261ea 100644 --- a/core/src/main/java/org/piccolo2d/activities/PActivityScheduler.java +++ b/core/src/main/java/org/piccolo2d/activities/PActivityScheduler.java @@ -58,10 +58,10 @@ public class PActivityScheduler implements Serializable { private static final long serialVersionUID = 1L; private transient Timer activityTimer = null; private final PRoot root; - private final List activities; + private final List activities; private boolean activitiesChanged; private boolean animating; - private final ArrayList processingActivities; + private final ArrayList processingActivities; /** * Constructs an instance of PActivityScheduler. All activities it will @@ -72,8 +72,8 @@ public class PActivityScheduler implements Serializable { */ public PActivityScheduler(final PRoot rootNode) { root = rootNode; - activities = new ArrayList(); - processingActivities = new ArrayList(); + activities = new ArrayList(); + processingActivities = new ArrayList(); } /** @@ -157,7 +157,7 @@ public void removeAllActivities() { * * @return reference to the current activities list. */ - public List getActivitiesReference() { + public List getActivitiesReference() { return activities; } diff --git a/extras/src/main/java/org/piccolo2d/extras/activities/PPathActivity.java b/core/src/main/java/org/piccolo2d/activities/PPathActivity.java similarity index 98% rename from extras/src/main/java/org/piccolo2d/extras/activities/PPathActivity.java rename to core/src/main/java/org/piccolo2d/activities/PPathActivity.java index 59866011..21399daa 100644 --- a/extras/src/main/java/org/piccolo2d/extras/activities/PPathActivity.java +++ b/core/src/main/java/org/piccolo2d/activities/PPathActivity.java @@ -26,9 +26,7 @@ * 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.activities; - -import org.piccolo2d.activities.PInterpolatingActivity; +package org.piccolo2d.activities; /** * PPathActivity is the abstract base class for all path activity diff --git a/extras/src/main/java/org/piccolo2d/extras/activities/PPositionPathActivity.java b/core/src/main/java/org/piccolo2d/activities/PPositionPathActivity.java similarity index 98% rename from extras/src/main/java/org/piccolo2d/extras/activities/PPositionPathActivity.java rename to core/src/main/java/org/piccolo2d/activities/PPositionPathActivity.java index 5c561cc8..46764096 100644 --- a/extras/src/main/java/org/piccolo2d/extras/activities/PPositionPathActivity.java +++ b/core/src/main/java/org/piccolo2d/activities/PPositionPathActivity.java @@ -26,15 +26,13 @@ * 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.activities; +package org.piccolo2d.activities; import java.awt.geom.GeneralPath; import java.awt.geom.PathIterator; import java.awt.geom.Point2D; import java.util.ArrayList; -import org.piccolo2d.activities.PInterpolatingActivity; - /** * PPositionPathActivity animates through a sequence of points. @@ -169,7 +167,7 @@ public void setPosition(final int index, final Point2D position) { */ public void setPositions(final GeneralPath path) { final PathIterator pi = path.getPathIterator(null, 1); - final ArrayList points = new ArrayList(); + final ArrayList points = new ArrayList(); final float[] point = new float[6]; float distanceSum = 0; float lastMoveToX = 0; diff --git a/core/src/main/java/org/piccolo2d/event/PDragEventHandler.java b/core/src/main/java/org/piccolo2d/event/PDragEventHandler.java index d63e1bca..29a08164 100644 --- a/core/src/main/java/org/piccolo2d/event/PDragEventHandler.java +++ b/core/src/main/java/org/piccolo2d/event/PDragEventHandler.java @@ -54,7 +54,7 @@ public PDragEventHandler() { draggedNode = null; raiseToTopOnPress = false; - setEventFilter(new PInputEventFilter(InputEvent.BUTTON1_MASK)); + setEventFilter(new PInputEventFilter(InputEvent.BUTTON1_DOWN_MASK)); } /** diff --git a/core/src/main/java/org/piccolo2d/event/PInputEvent.java b/core/src/main/java/org/piccolo2d/event/PInputEvent.java index dd89e649..cdede747 100644 --- a/core/src/main/java/org/piccolo2d/event/PInputEvent.java +++ b/core/src/main/java/org/piccolo2d/event/PInputEvent.java @@ -252,12 +252,14 @@ public boolean isActionKey() { * * @return modifier flags for the input event */ - public int getModifiers() { + /* + private int getModifiers() { if (!isFocusEvent()) { return inputEvent.getModifiers(); } throw new IllegalStateException("Can't get modifiers from focus event"); } + */ /** * Returns the extended modifiers provided for the input event by swing. diff --git a/core/src/main/java/org/piccolo2d/event/PInputEventFilter.java b/core/src/main/java/org/piccolo2d/event/PInputEventFilter.java index 88a6fb1c..72e23574 100644 --- a/core/src/main/java/org/piccolo2d/event/PInputEventFilter.java +++ b/core/src/main/java/org/piccolo2d/event/PInputEventFilter.java @@ -53,9 +53,9 @@ */ public class PInputEventFilter { /** Mask representing all possible modifiers. */ - public static final int ALL_MODIFIERS_MASK = InputEvent.BUTTON1_MASK | InputEvent.BUTTON2_MASK | InputEvent.BUTTON3_MASK - | InputEvent.SHIFT_MASK | InputEvent.CTRL_MASK | InputEvent.ALT_MASK | InputEvent.ALT_GRAPH_MASK - | InputEvent.META_MASK; + public static final int ALL_MODIFIERS_MASK = InputEvent.BUTTON1_DOWN_MASK | InputEvent.BUTTON2_DOWN_MASK | InputEvent.BUTTON3_DOWN_MASK + | InputEvent.SHIFT_DOWN_MASK | InputEvent.CTRL_DOWN_MASK | InputEvent.ALT_DOWN_MASK | InputEvent.ALT_GRAPH_DOWN_MASK + | InputEvent.META_DOWN_MASK; /** If event modifiers don't match this exactly, event it filtered. */ private int andMask; @@ -153,7 +153,7 @@ public boolean acceptsEvent(final PInputEvent event, final int type) { int modifiers = 0; if (!event.isFocusEvent()) { - modifiers = event.getModifiers(); + modifiers = event.getModifiersEx(); } if (event.isHandled() && !acceptsAlreadyHandledEvents) { diff --git a/extras/src/main/java/org/piccolo2d/extras/event/PNavigationEventHandler.java b/core/src/main/java/org/piccolo2d/event/PNavigationEventHandler.java similarity index 95% rename from extras/src/main/java/org/piccolo2d/extras/event/PNavigationEventHandler.java rename to core/src/main/java/org/piccolo2d/event/PNavigationEventHandler.java index d3d5399f..49dfbda8 100644 --- a/extras/src/main/java/org/piccolo2d/extras/event/PNavigationEventHandler.java +++ b/core/src/main/java/org/piccolo2d/event/PNavigationEventHandler.java @@ -26,7 +26,7 @@ * 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.event; +package org.piccolo2d.event; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; @@ -43,9 +43,6 @@ import org.piccolo2d.PNode; import org.piccolo2d.activities.PActivity; import org.piccolo2d.activities.PTransformActivity; -import org.piccolo2d.event.PBasicInputEventHandler; -import org.piccolo2d.event.PInputEvent; -import org.piccolo2d.event.PInputEventFilter; import org.piccolo2d.util.PBounds; import org.piccolo2d.util.PDimension; @@ -77,7 +74,7 @@ public class PNavigationEventHandler extends PBasicInputEventHandler { /** The OUT direction on the scene. */ public static final int OUT = 5; - private static Hashtable NODE_TO_GLOBAL_NODE_CENTER_MAPPING = new Hashtable(); + private static Hashtable NODE_TO_GLOBAL_NODE_CENTER_MAPPING = new Hashtable(); private PNode focusNode; private PTransformActivity navigationActivity; @@ -88,7 +85,7 @@ public class PNavigationEventHandler extends PBasicInputEventHandler { */ public PNavigationEventHandler() { super(); - setEventFilter(new PInputEventFilter(InputEvent.BUTTON1_MASK)); + setEventFilter(new PInputEventFilter(InputEvent.BUTTON1_DOWN_MASK)); } // **************************************************************** @@ -263,12 +260,12 @@ public PNode getNeighborInDirection(final int direction) { final Point2D highlightCenter = focusNode.getGlobalFullBounds().getCenter2D(); NODE_TO_GLOBAL_NODE_CENTER_MAPPING.put(focusNode, highlightCenter); - final List l = getNeighbors(); + final List l = getNeighbors(); sortNodesByDistanceFromPoint(l, highlightCenter); - final Iterator i = l.iterator(); + final Iterator i = l.iterator(); while (i.hasNext()) { - final PNode each = (PNode) i.next(); + final PNode each = i.next(); if (nodeIsNeighborInDirection(each, direction)) { return each; } @@ -283,15 +280,15 @@ public PNode getNeighborInDirection(final int direction) { * * @return list of nodes that are 1 hop away from the current focusNode */ - public List getNeighbors() { - final ArrayList result = new ArrayList(); + public List getNeighbors() { + final ArrayList result = new ArrayList(); if (focusNode == null || focusNode.getParent() == null) { return result; } final PNode focusParent = focusNode.getParent(); - final Iterator i = focusParent.getChildrenIterator(); + final Iterator i = focusParent.getChildrenIterator(); while (i.hasNext()) { final PNode each = (PNode) i.next(); @@ -363,13 +360,15 @@ public boolean nodeIsNeighborInDirection(final PNode node, final int direction) * @param nodes list of nodes to be sorted * @param point point from which distance is being computed */ - public void sortNodesByDistanceFromPoint(final List nodes, final Point2D point) { - Collections.sort(nodes, new Comparator() { - public int compare(final Object o1, final Object o2) { - return compare((PNode) o1, (PNode) o2); - } - - private int compare(final PNode each1, final PNode each2) { + public void sortNodesByDistanceFromPoint(final List nodes, final Point2D point) { + + Collections.sort(nodes, new Comparator() { + + //public int compare(final Object o1, final Object o2) { + // return compare((PNode) o1, (PNode) o2); + //} + + public int compare(final PNode each1, final PNode each2) { final Point2D center1 = each1.getGlobalFullBounds().getCenter2D(); final Point2D center2 = each2.getGlobalFullBounds().getCenter2D(); diff --git a/extras/src/main/java/org/piccolo2d/extras/event/PNotification.java b/core/src/main/java/org/piccolo2d/event/PNotification.java similarity index 60% rename from extras/src/main/java/org/piccolo2d/extras/event/PNotification.java rename to core/src/main/java/org/piccolo2d/event/PNotification.java index 1fabb7c5..f567ab52 100644 --- a/extras/src/main/java/org/piccolo2d/extras/event/PNotification.java +++ b/core/src/main/java/org/piccolo2d/event/PNotification.java @@ -33,7 +33,7 @@ * Wotonomy: OpenStep design patterns for pure Java * applications. Copyright (C) 2000 Blacksmith, Inc. */ -package org.piccolo2d.extras.event; +package org.piccolo2d.event; import java.util.Map; @@ -54,56 +54,67 @@ * @author Jesse Grosjean */ public class PNotification { - /** Name of the notification. */ - protected String name; - /** The Object associated with this notification. */ - protected Object source; - /** A free form map of properties to attach to this notification. */ - protected Map properties; + /** Name of the notification. */ + protected String name; + /** The Object associated with this notification. */ + protected Object source; + /** A free form map of properties to attach to this notification. */ + protected Map properties; - /** - * Creates a notification. - * - * @param name Arbitrary name of the notification - * @param source object associated with this notification - * @param properties free form map of information about the notification - */ - public PNotification(final String name, final Object source, final Map properties) { - this.name = name; - this.source = source; - this.properties = properties; - } + /** + * Creates a notification. + * + * @param name Arbitrary name of the notification + * @param source object associated with this notification + */ + public PNotification(final String name, final Object source) { + this.name = name; + this.source = source; + } - /** - * Return the name of the notification. This is the same as the name used to - * register with the notification center. - * - * @return name of notification - */ - public String getName() { - return name; - } + /** + * Creates a notification. + * + * @param name Arbitrary name of the notification + * @param source object associated with this notification + * @param properties free form map of information about the notification + */ + protected PNotification(final String name, final Object source, final Map properties) { + this.name = name; + this.source = source; + this.properties = properties; + } - /** - * Return the object associated with this notification. This is most often - * the same object that posted the notification. It may be null. - * - * @return object associated with this notification - */ - public Object getObject() { - return source; - } + /** + * Return the name of the notification. This is the same as the name used to + * register with the notification center. + * + * @return name of notification + */ + public String getName() { + return name; + } - /** - * Return a property associated with the notification, or null if not found. - * - * @param key key used for looking up the property - * @return value associated with the key or null if not found - */ - public Object getProperty(final Object key) { - if (properties != null) { - return properties.get(key); - } - return null; - } + /** + * Return the object associated with this notification. This is most often the + * same object that posted the notification. It may be null. + * + * @return object associated with this notification + */ + public Object getObject() { + return source; + } + + /** + * Return a property associated with the notification, or null if not found. + * + * @param key key used for looking up the property + * @return value associated with the key or null if not found + */ + public Object getProperty(final Object key) { + if (properties != null) { + return properties.get(key); + } + return null; + } } diff --git a/extras/src/main/java/org/piccolo2d/extras/event/PNotificationCenter.java b/core/src/main/java/org/piccolo2d/event/PNotificationCenter.java similarity index 91% rename from extras/src/main/java/org/piccolo2d/extras/event/PNotificationCenter.java rename to core/src/main/java/org/piccolo2d/event/PNotificationCenter.java index b10acd08..def257df 100644 --- a/extras/src/main/java/org/piccolo2d/extras/event/PNotificationCenter.java +++ b/core/src/main/java/org/piccolo2d/event/PNotificationCenter.java @@ -33,7 +33,7 @@ * Wotonomy: OpenStep design patterns for pure Java * applications. Copyright (C) 2000 Blacksmith, Inc. */ -package org.piccolo2d.extras.event; +package org.piccolo2d.event; import java.lang.ref.ReferenceQueue; import java.lang.ref.WeakReference; @@ -46,6 +46,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Set; /** * PNotificationCenter provides a way for objects that don't know about @@ -69,10 +70,10 @@ public final class PNotificationCenter { private static volatile PNotificationCenter DEFAULT_CENTER; /** A map of listeners keyed by NotificationKey objects. */ - private HashMap listenersMap; + private HashMap> listenersMap; /** A queue of NotificationKeys that are available to be garbage collected. */ - private ReferenceQueue keyQueue; + private ReferenceQueue keyQueue; /** * Singleton accessor for the PNotificationCenter. @@ -87,8 +88,8 @@ public synchronized static PNotificationCenter defaultCenter() { } private PNotificationCenter() { - listenersMap = new HashMap(); - keyQueue = new ReferenceQueue(); + listenersMap = new HashMap>(); + keyQueue = new ReferenceQueue(); } /** @@ -122,9 +123,9 @@ public boolean addListener(final Object listener, final String callbackMethodNam final NotificationKey key = new NotificationKey(name, sanitizedObject); final NotificationTarget notificationTarget = new NotificationTarget(listener, method); - List list = (List) listenersMap.get(key); + List list = listenersMap.get(key); if (list == null) { - list = new ArrayList(); + list = new ArrayList(); listenersMap.put(new NotificationKey(name, sanitizedObject, keyQueue), list); } @@ -138,7 +139,7 @@ public boolean addListener(final Object listener, final String callbackMethodNam private Method extractCallbackMethod(final Object listener, final String methodName) { Method method = null; try { - Class[] classes = new Class[1]; + Class[] classes = new Class[1]; classes[0] = PNotification.class; method = listener.getClass().getMethod(methodName, classes); } @@ -183,7 +184,8 @@ private Object nullify(final Object object) { public void removeListener(final Object listener) { processKeyQueue(); - final Iterator i = new LinkedList(listenersMap.keySet()).iterator(); + Set keys = listenersMap.keySet(); + final Iterator i = new LinkedList(keys).iterator(); while (i.hasNext()) { removeListener(listener, i.next()); } @@ -209,8 +211,8 @@ public void removeListener(final Object listener) { public void removeListener(final Object listener, final String notificationName, final Object object) { processKeyQueue(); - final List keys = matchingKeys(notificationName, object); - final Iterator it = keys.iterator(); + final List keys = matchingKeys(notificationName, object); + final Iterator it = keys.iterator(); while (it.hasNext()) { removeListener(listener, it.next()); } @@ -240,7 +242,7 @@ public void postNotification(final String notificationName, final Object object) * @param object source of the notification, may be null * @param properties properties associated with the notification */ - public void postNotification(final String notificationName, final Object object, final Map properties) { + public void postNotification(final String notificationName, final Object object, final Map properties) { postNotification(new PNotification(notificationName, object, properties)); } @@ -253,7 +255,7 @@ public void postNotification(final String notificationName, final Object object, * listeners */ public void postNotification(final PNotification notification) { - final List mergedListeners = new LinkedList(); + final List mergedListeners = new LinkedList<>(); final Object name = notification.getName(); final Object object = notification.getObject(); @@ -283,20 +285,20 @@ else if (object != null) { * @param object source of the notification * @param listeners list to append listeners to */ - private void fillWithMatchingListeners(final Object notificationName, final Object object, final List listeners) { + private void fillWithMatchingListeners(final Object notificationName, final Object object, final List listeners) { final Object key = new NotificationKey(nullify(notificationName), nullify(object)); - final List globalListeners = (List) listenersMap.get(key); + final List globalListeners = listenersMap.get(key); if (globalListeners != null) { listeners.addAll(globalListeners); } } - private void dispatchNotifications(final PNotification notification, final List listeners) { + private void dispatchNotifications(final PNotification notification, final List listeners) { NotificationTarget listener; - final Iterator listenerIterator = listeners.iterator(); + final Iterator listenerIterator = listeners.iterator(); while (listenerIterator.hasNext()) { - listener = (NotificationTarget) listenerIterator.next(); + listener = listenerIterator.next(); if (listener.get() == null) { listenerIterator.remove(); } @@ -328,13 +330,13 @@ private void notifyListener(final PNotification notification, final Notification * * @return list of matching keys */ - private List matchingKeys(final String name, final Object object) { - final List result = new LinkedList(); + private List matchingKeys(final String name, final Object object) { + final List result = new LinkedList(); final NotificationKey searchKey = new NotificationKey(name, object); - final Iterator it = listenersMap.keySet().iterator(); + final Iterator it = listenersMap.keySet().iterator(); while (it.hasNext()) { - final NotificationKey key = (NotificationKey) it.next(); + final NotificationKey key = it.next(); if (searchKey.equals(key)) { result.add(key); } @@ -356,14 +358,14 @@ private void removeListener(final Object listener, final Object key) { return; } - final List list = (List) listenersMap.get(key); + final List list = listenersMap.get(key); if (list == null) { return; } - final Iterator it = list.iterator(); + final Iterator it = list.iterator(); while (it.hasNext()) { - final Object observer = ((NotificationTarget) it.next()).get(); + final Object observer = it.next().get(); if (observer == null || listener == observer) { it.remove(); } diff --git a/core/src/main/java/org/piccolo2d/event/PPanEventHandler.java b/core/src/main/java/org/piccolo2d/event/PPanEventHandler.java index 157b5ae3..95794ddf 100644 --- a/core/src/main/java/org/piccolo2d/event/PPanEventHandler.java +++ b/core/src/main/java/org/piccolo2d/event/PPanEventHandler.java @@ -61,7 +61,7 @@ public class PPanEventHandler extends PDragSequenceEventHandler { */ public PPanEventHandler() { super(); - setEventFilter(new PInputEventFilter(InputEvent.BUTTON1_MASK)); + setEventFilter(new PInputEventFilter(InputEvent.BUTTON1_DOWN_MASK)); setAutopan(true); } diff --git a/extras/src/main/java/org/piccolo2d/extras/event/PSelectionEventHandler.java b/core/src/main/java/org/piccolo2d/event/PSelectionEventHandler.java similarity index 91% rename from extras/src/main/java/org/piccolo2d/extras/event/PSelectionEventHandler.java rename to core/src/main/java/org/piccolo2d/event/PSelectionEventHandler.java index 5ae38766..1477f6b2 100644 --- a/extras/src/main/java/org/piccolo2d/extras/event/PSelectionEventHandler.java +++ b/core/src/main/java/org/piccolo2d/event/PSelectionEventHandler.java @@ -26,7 +26,7 @@ * 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.event; +package org.piccolo2d.event; import java.awt.BasicStroke; import java.awt.Color; @@ -45,9 +45,7 @@ import org.piccolo2d.PCamera; import org.piccolo2d.PLayer; import org.piccolo2d.PNode; -import org.piccolo2d.event.PDragSequenceEventHandler; -import org.piccolo2d.event.PInputEvent; -import org.piccolo2d.extras.handles.PBoundsHandle; +import org.piccolo2d.handles.PBoundsHandle; import org.piccolo2d.nodes.PPath; import org.piccolo2d.util.PBounds; import org.piccolo2d.util.PDimension; @@ -76,9 +74,9 @@ public class PSelectionEventHandler extends PDragSequenceEventHandler { static final int NUM_STROKES = 10; /** The current selection. */ - private HashMap selection = null; + private HashMap selection = null; /** List of nodes whose children can be selected. */ - private List selectableParents = null; + private List selectableParents = null; private PPath marquee = null; /** Node that marquee is added to as a child. */ @@ -90,11 +88,11 @@ public class PSelectionEventHandler extends PDragSequenceEventHandler { private Stroke[] strokes = null; /** Used within drag handler temporarily. */ - private HashMap allItems = null; + private HashMap allItems = null; /** Used within drag handler temporarily. */ - private ArrayList unselectList = null; - private HashMap marqueeMap = null; + private ArrayList unselectList = null; + private HashMap marqueeMap = null; /** Node pressed on (or null if none). */ private PNode pressNode = null; @@ -118,7 +116,7 @@ public class PSelectionEventHandler extends PDragSequenceEventHandler { */ public PSelectionEventHandler(final PNode marqueeParent, final PNode selectableParent) { this.marqueeParent = marqueeParent; - selectableParents = new ArrayList(); + selectableParents = new ArrayList(); selectableParents.add(selectableParent); init(); } @@ -131,7 +129,7 @@ public PSelectionEventHandler(final PNode marqueeParent, final PNode selectableP * @param selectableParents A list of nodes whose children will be selected * by this event handler. */ - public PSelectionEventHandler(final PNode marqueeParent, final List selectableParents) { + public PSelectionEventHandler(final PNode marqueeParent, final List selectableParents) { this.marqueeParent = marqueeParent; this.selectableParents = selectableParents; init(); @@ -150,10 +148,10 @@ protected void init() { strokes[i] = new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 1, dash, i); } - selection = new HashMap(); - allItems = new HashMap(); - unselectList = new ArrayList(); - marqueeMap = new HashMap(); + selection = new HashMap(); + allItems = new HashMap(); + unselectList = new ArrayList(); + marqueeMap = new HashMap(); } /** @@ -161,11 +159,11 @@ protected void init() { * * @param items collection of items to be selected */ - public void select(final Collection items) { + public void select(final Collection items) { boolean changes = false; - final Iterator itemIt = items.iterator(); + final Iterator itemIt = items.iterator(); while (itemIt.hasNext()) { - final PNode node = (PNode) itemIt.next(); + final PNode node = itemIt.next(); changes |= internalSelect(node); } if (changes) { @@ -178,7 +176,7 @@ public void select(final Collection items) { * * @param items map where keys are to be selected */ - public void select(final Map items) { + public void select(final Map items) { select(items.keySet()); } @@ -230,11 +228,11 @@ public void decorateSelectedNode(final PNode node) { * * @param items items to remove form the selection */ - public void unselect(final Collection items) { + public void unselect(final Collection items) { boolean changes = false; - final Iterator itemIt = items.iterator(); + final Iterator itemIt = items.iterator(); while (itemIt.hasNext()) { - final PNode node = (PNode) itemIt.next(); + final PNode node = itemIt.next(); changes |= internalUnselect(node); } if (changes) { @@ -285,7 +283,7 @@ public void undecorateSelectedNode(final PNode node) { public void unselectAll() { // Because unselect() removes from selection, we need to // take a copy of it first so it isn't changed while we're iterating - final ArrayList sel = new ArrayList(selection.keySet()); + final ArrayList sel = new ArrayList(selection.keySet()); unselect(sel); } @@ -304,8 +302,8 @@ public boolean isSelected(final PNode node) { * * @return copy of selection */ - public Collection getSelection() { - return new ArrayList(selection.keySet()); + public Collection getSelection() { + return new ArrayList(selection.keySet()); } /** @@ -314,7 +312,7 @@ public Collection getSelection() { * * @return direct reference to selection */ - public Collection getSelectionReference() { + public Collection getSelectionReference() { return Collections.unmodifiableCollection(selection.keySet()); } @@ -328,7 +326,7 @@ public Collection getSelectionReference() { protected boolean isSelectable(final PNode node) { boolean selectable = false; - final Iterator parentsIt = selectableParents.iterator(); + final Iterator parentsIt = selectableParents.iterator(); while (parentsIt.hasNext()) { final PNode parent = (PNode) parentsIt.next(); if (parent.getChildrenReference().contains(node)) { @@ -385,7 +383,7 @@ public void setSelectableParent(final PNode node) { * * @param c nodes to become selectable parents. */ - public void setSelectableParents(final Collection c) { + public void setSelectableParents(final Collection c) { selectableParents.clear(); selectableParents.addAll(c); } @@ -395,8 +393,8 @@ public void setSelectableParents(final Collection c) { * * @return selectable parents */ - public Collection getSelectableParents() { - return new ArrayList(selectableParents); + public Collection getSelectableParents() { + return new ArrayList(selectableParents); } // ////////////////////////////////////////////////////// @@ -608,13 +606,13 @@ protected void updateMarquee(final PInputEvent pie) { allItems.clear(); final PNodeFilter filter = createNodeFilter(b); - final Iterator parentsIt = selectableParents.iterator(); + final Iterator parentsIt = selectableParents.iterator(); while (parentsIt.hasNext()) { final PNode parent = (PNode) parentsIt.next(); - Collection items; + Collection items; if (parent instanceof PCamera) { - items = new ArrayList(); + items = new ArrayList(); for (int i = 0; i < ((PCamera) parent).getLayerCount(); i++) { ((PCamera) parent).getLayer(i).getAllNodes(filter, items); } @@ -623,7 +621,7 @@ protected void updateMarquee(final PInputEvent pie) { items = parent.getAllNodes(filter, null); } - final Iterator itemsIt = items.iterator(); + final Iterator itemsIt = items.iterator(); while (itemsIt.hasNext()) { allItems.put(itemsIt.next(), Boolean.TRUE); } @@ -639,9 +637,9 @@ protected void computeMarqueeSelection(final PInputEvent pie) { unselectList.clear(); // Make just the items in the list selected // Do this efficiently by first unselecting things not in the list - Iterator selectionEn = selection.keySet().iterator(); + Iterator selectionEn = selection.keySet().iterator(); while (selectionEn.hasNext()) { - final PNode node = (PNode) selectionEn.next(); + final PNode node = selectionEn.next(); if (!allItems.containsKey(node)) { unselectList.add(node); } @@ -651,7 +649,7 @@ protected void computeMarqueeSelection(final PInputEvent pie) { // Then select the rest selectionEn = allItems.keySet().iterator(); while (selectionEn.hasNext()) { - final PNode node = (PNode) selectionEn.next(); + final PNode node = selectionEn.next(); if (!selection.containsKey(node) && !marqueeMap.containsKey(node) && isSelectable(node)) { marqueeMap.put(node, Boolean.TRUE); } @@ -670,9 +668,9 @@ else if (!isSelectable(node)) { */ protected void computeOptionMarqueeSelection(final PInputEvent pie) { unselectList.clear(); - Iterator selectionEn = selection.keySet().iterator(); + Iterator selectionEn = selection.keySet().iterator(); while (selectionEn.hasNext()) { - final PNode node = (PNode) selectionEn.next(); + final PNode node = selectionEn.next(); if (!allItems.containsKey(node) && marqueeMap.containsKey(node)) { marqueeMap.remove(node); unselectList.add(node); @@ -683,7 +681,7 @@ protected void computeOptionMarqueeSelection(final PInputEvent pie) { // Then select the rest selectionEn = allItems.keySet().iterator(); while (selectionEn.hasNext()) { - final PNode node = (PNode) selectionEn.next(); + final PNode node = selectionEn.next(); if (!selection.containsKey(node) && !marqueeMap.containsKey(node) && isSelectable(node)) { marqueeMap.put(node, Boolean.TRUE); } @@ -730,9 +728,9 @@ protected void dragStandardSelection(final PInputEvent e) { e.getTopCamera().localToView(d); final PDimension gDist = new PDimension(); - final Iterator selectionEn = getSelection().iterator(); + final Iterator selectionEn = getSelection().iterator(); while (selectionEn.hasNext()) { - final PNode node = (PNode) selectionEn.next(); + final PNode node = selectionEn.next(); gDist.setSize(d); node.getParent().globalToLocal(gDist); @@ -787,9 +785,9 @@ protected void dragActivityStep(final PInputEvent aEvent) { */ public void keyPressed(final PInputEvent e) { if (e.getKeyCode() == KeyEvent.VK_DELETE && deleteKeyActive && !selection.isEmpty()) { - final Iterator selectionEn = selection.keySet().iterator(); + final Iterator selectionEn = selection.keySet().iterator(); while (selectionEn.hasNext()) { - final PNode node = (PNode) selectionEn.next(); + final PNode node = selectionEn.next(); node.removeFromParent(); } selection.clear(); @@ -874,8 +872,8 @@ public boolean acceptChildrenOf(final PNode node) { */ public boolean isCameraLayer(final PNode node) { if (node instanceof PLayer) { - for (final Iterator i = selectableParents.iterator(); i.hasNext();) { - final PNode parent = (PNode) i.next(); + for (final Iterator i = selectableParents.iterator(); i.hasNext();) { + final PNode parent = i.next(); if (parent instanceof PCamera && ((PCamera) parent).indexOfLayer((PLayer) node) != -1) { return true; } diff --git a/extras/src/main/java/org/piccolo2d/extras/event/PStyledTextEventHandler.java b/core/src/main/java/org/piccolo2d/event/PStyledTextEventHandler.java similarity index 96% rename from extras/src/main/java/org/piccolo2d/extras/event/PStyledTextEventHandler.java rename to core/src/main/java/org/piccolo2d/event/PStyledTextEventHandler.java index f99895c9..a28662db 100644 --- a/extras/src/main/java/org/piccolo2d/extras/event/PStyledTextEventHandler.java +++ b/core/src/main/java/org/piccolo2d/event/PStyledTextEventHandler.java @@ -26,7 +26,7 @@ * 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.event; +package org.piccolo2d.event; import java.awt.Color; import java.awt.Dimension; @@ -55,10 +55,7 @@ import org.piccolo2d.PCamera; import org.piccolo2d.PCanvas; import org.piccolo2d.PNode; -import org.piccolo2d.event.PBasicInputEventHandler; -import org.piccolo2d.event.PInputEvent; -import org.piccolo2d.event.PInputEventFilter; -import org.piccolo2d.extras.nodes.PStyledText; +import org.piccolo2d.nodes.PStyledText; /** @@ -89,7 +86,7 @@ public class PStyledTextEventHandler extends PBasicInputEventHandler { */ public PStyledTextEventHandler(final PCanvas canvas) { final PInputEventFilter filter = new PInputEventFilter(); - filter.setOrMask(InputEvent.BUTTON1_MASK | InputEvent.BUTTON3_MASK); + filter.setOrMask(InputEvent.BUTTON1_DOWN_MASK | InputEvent.BUTTON3_DOWN_MASK); setEventFilter(filter); this.canvas = canvas; initEditor(createDefaultEditor()); @@ -173,7 +170,7 @@ public PStyledText createText() { final Font eFont = editor.getFont(); final SimpleAttributeSet sas = new SimpleAttributeSet(); sas.addAttribute(StyleConstants.FontFamily, eFont.getFamily()); - sas.addAttribute(StyleConstants.FontSize, new Integer(eFont.getSize())); + sas.addAttribute(StyleConstants.FontSize, Integer.valueOf(eFont.getSize())); ((StyledDocument) doc).setParagraphAttributes(0, doc.getLength(), sas, false); } @@ -290,8 +287,8 @@ public void run() { SwingUtilities.invokeLater(new Runnable() { public void run() { final MouseEvent me = new MouseEvent(editor, MouseEvent.MOUSE_PRESSED, event.getWhen(), event - .getModifiers() - | InputEvent.BUTTON1_MASK, (int) (event.getCanvasPosition().getX() - editor.getX()), + .getModifiersEx() + | InputEvent.BUTTON1_DOWN_MASK, (int) (event.getCanvasPosition().getX() - editor.getX()), (int) (event.getCanvasPosition().getY() - editor.getY()), 1, false); editor.dispatchEvent(me); } diff --git a/core/src/main/java/org/piccolo2d/event/PZoomEventHandler.java b/core/src/main/java/org/piccolo2d/event/PZoomEventHandler.java index 057d379a..1d137418 100644 --- a/core/src/main/java/org/piccolo2d/event/PZoomEventHandler.java +++ b/core/src/main/java/org/piccolo2d/event/PZoomEventHandler.java @@ -72,7 +72,7 @@ public class PZoomEventHandler extends PDragSequenceEventHandler { */ public PZoomEventHandler() { super(); - setEventFilter(new PInputEventFilter(InputEvent.BUTTON3_MASK)); + setEventFilter(new PInputEventFilter(InputEvent.BUTTON3_DOWN_MASK)); } // **************************************************************** diff --git a/extras/src/main/java/org/piccolo2d/extras/event/PZoomToEventHandler.java b/core/src/main/java/org/piccolo2d/event/PZoomToEventHandler.java similarity index 94% rename from extras/src/main/java/org/piccolo2d/extras/event/PZoomToEventHandler.java rename to core/src/main/java/org/piccolo2d/event/PZoomToEventHandler.java index 150209a3..15df29b4 100644 --- a/extras/src/main/java/org/piccolo2d/extras/event/PZoomToEventHandler.java +++ b/core/src/main/java/org/piccolo2d/event/PZoomToEventHandler.java @@ -26,15 +26,12 @@ * 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.event; +package org.piccolo2d.event; import java.awt.event.InputEvent; import org.piccolo2d.PCamera; import org.piccolo2d.PNode; -import org.piccolo2d.event.PBasicInputEventHandler; -import org.piccolo2d.event.PInputEvent; -import org.piccolo2d.event.PInputEventFilter; import org.piccolo2d.util.PBounds; @@ -52,7 +49,7 @@ public class PZoomToEventHandler extends PBasicInputEventHandler { * Constructs a PZoomToEventHandler that only recognizes BUTTON1 events. */ public PZoomToEventHandler() { - setEventFilter(new PInputEventFilter(InputEvent.BUTTON1_MASK)); + setEventFilter(new PInputEventFilter(InputEvent.BUTTON1_DOWN_MASK)); } /** diff --git a/extras/src/main/java/org/piccolo2d/extras/handles/PBoundsHandle.java b/core/src/main/java/org/piccolo2d/handles/PBoundsHandle.java similarity index 98% rename from extras/src/main/java/org/piccolo2d/extras/handles/PBoundsHandle.java rename to core/src/main/java/org/piccolo2d/handles/PBoundsHandle.java index 6a4931c0..b220a6ec 100644 --- a/extras/src/main/java/org/piccolo2d/extras/handles/PBoundsHandle.java +++ b/core/src/main/java/org/piccolo2d/handles/PBoundsHandle.java @@ -26,7 +26,7 @@ * 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.handles; +package org.piccolo2d.handles; import java.awt.Cursor; import java.awt.geom.Point2D; @@ -39,8 +39,8 @@ 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.PBoundsLocator; import org.piccolo2d.util.PDimension; import org.piccolo2d.util.PPickPath; @@ -104,11 +104,11 @@ public static void addStickyBoundsHandlesTo(final PNode node, final PCamera came * @param node node having its handles removed from */ public static void removeBoundsHandlesFrom(final PNode node) { - final ArrayList handles = new ArrayList(); + final ArrayList handles = new ArrayList(); - final Iterator i = node.getChildrenIterator(); + final Iterator i = node.getChildrenIterator(); while (i.hasNext()) { - final PNode each = (PNode) i.next(); + final PNode each = i.next(); if (each instanceof PBoundsHandle) { handles.add(each); } @@ -259,7 +259,7 @@ public void endHandleDrag(final Point2D aLocalPoint, final PInputEvent aEvent) { * @param flipY whether to allow flipping along the y direction */ public void flipSiblingBoundsHandles(final boolean flipX, final boolean flipY) { - final Iterator i = getParent().getChildrenIterator(); + final Iterator i = getParent().getChildrenIterator(); while (i.hasNext()) { final Object each = i.next(); if (each instanceof PBoundsHandle) { diff --git a/extras/src/main/java/org/piccolo2d/extras/handles/PHandle.java b/core/src/main/java/org/piccolo2d/handles/PHandle.java similarity index 98% rename from extras/src/main/java/org/piccolo2d/extras/handles/PHandle.java rename to core/src/main/java/org/piccolo2d/handles/PHandle.java index 6669d961..71d58c49 100644 --- a/extras/src/main/java/org/piccolo2d/extras/handles/PHandle.java +++ b/core/src/main/java/org/piccolo2d/handles/PHandle.java @@ -26,7 +26,7 @@ * 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.handles; +package org.piccolo2d.handles; import java.awt.Color; import java.awt.Shape; @@ -43,11 +43,11 @@ 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.nodes.PPath; import org.piccolo2d.util.PBounds; import org.piccolo2d.util.PDimension; +import org.piccolo2d.util.PLocator; +import org.piccolo2d.util.PNodeLocator; /** @@ -118,7 +118,7 @@ public void propertyChange(final PropertyChangeEvent evt) { } }); - handleDragger.setEventFilter(new PInputEventFilter(InputEvent.BUTTON1_MASK)); + handleDragger.setEventFilter(new PInputEventFilter(InputEvent.BUTTON1_DOWN_MASK)); handleDragger.getEventFilter().setMarksAcceptedEventsAsHandled(true); handleDragger.getEventFilter().setAcceptsMouseEntered(false); handleDragger.getEventFilter().setAcceptsMouseExited(false); diff --git a/extras/src/main/java/org/piccolo2d/extras/handles/PStickyHandleManager.java b/core/src/main/java/org/piccolo2d/handles/PStickyHandleManager.java similarity index 99% rename from extras/src/main/java/org/piccolo2d/extras/handles/PStickyHandleManager.java rename to core/src/main/java/org/piccolo2d/handles/PStickyHandleManager.java index 42f7017e..20b8f1ea 100644 --- a/extras/src/main/java/org/piccolo2d/extras/handles/PStickyHandleManager.java +++ b/core/src/main/java/org/piccolo2d/handles/PStickyHandleManager.java @@ -26,7 +26,7 @@ * 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.handles; +package org.piccolo2d.handles; import org.piccolo2d.PCamera; import org.piccolo2d.PNode; diff --git a/extras/src/main/java/org/piccolo2d/extras/handles/package.html b/core/src/main/java/org/piccolo2d/handles/package.html similarity index 100% rename from extras/src/main/java/org/piccolo2d/extras/handles/package.html rename to core/src/main/java/org/piccolo2d/handles/package.html diff --git a/extras/src/main/java/org/piccolo2d/extras/nodes/P3DRect.java b/core/src/main/java/org/piccolo2d/nodes/P3DRect.java similarity index 98% rename from extras/src/main/java/org/piccolo2d/extras/nodes/P3DRect.java rename to core/src/main/java/org/piccolo2d/nodes/P3DRect.java index 3a415656..b848bcc2 100644 --- a/extras/src/main/java/org/piccolo2d/extras/nodes/P3DRect.java +++ b/core/src/main/java/org/piccolo2d/nodes/P3DRect.java @@ -26,7 +26,7 @@ * 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.nodes; +package org.piccolo2d.nodes; import java.awt.BasicStroke; import java.awt.Color; @@ -116,7 +116,7 @@ public boolean getRaised() { * * @param paintContext context in which the paiting should occur */ - protected void paint(final PPaintContext paintContext) { + public void paint(final PPaintContext paintContext) { // lazy init: if (stroke == null) { stroke = new BasicStroke(0); diff --git a/core/src/main/java/org/piccolo2d/nodes/PArea.java b/core/src/main/java/org/piccolo2d/nodes/PArea.java index 6a511253..a7f1a823 100644 --- a/core/src/main/java/org/piccolo2d/nodes/PArea.java +++ b/core/src/main/java/org/piccolo2d/nodes/PArea.java @@ -38,6 +38,8 @@ * Area node. */ public final class PArea extends PShape { + + private static final long serialVersionUID = 1L; /** Area for this area node. */ private final transient Area area; diff --git a/extras/src/main/java/org/piccolo2d/extras/nodes/PCacheCamera.java b/core/src/main/java/org/piccolo2d/nodes/PCacheCamera.java similarity index 99% rename from extras/src/main/java/org/piccolo2d/extras/nodes/PCacheCamera.java rename to core/src/main/java/org/piccolo2d/nodes/PCacheCamera.java index 961029b8..5fe4cb89 100644 --- a/extras/src/main/java/org/piccolo2d/extras/nodes/PCacheCamera.java +++ b/core/src/main/java/org/piccolo2d/nodes/PCacheCamera.java @@ -26,7 +26,7 @@ * 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.nodes; +package org.piccolo2d.nodes; import java.awt.Color; import java.awt.GraphicsEnvironment; diff --git a/extras/src/main/java/org/piccolo2d/extras/nodes/PClip.java b/core/src/main/java/org/piccolo2d/nodes/PClip.java similarity index 98% rename from extras/src/main/java/org/piccolo2d/extras/nodes/PClip.java rename to core/src/main/java/org/piccolo2d/nodes/PClip.java index 946257ca..5db2a0ea 100644 --- a/extras/src/main/java/org/piccolo2d/extras/nodes/PClip.java +++ b/core/src/main/java/org/piccolo2d/nodes/PClip.java @@ -26,14 +26,13 @@ * 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.nodes; +package org.piccolo2d.nodes; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.geom.Rectangle2D; import org.piccolo2d.PNode; -import org.piccolo2d.nodes.PPath; import org.piccolo2d.util.PBounds; import org.piccolo2d.util.PPaintContext; import org.piccolo2d.util.PPickPath; diff --git a/extras/src/main/java/org/piccolo2d/extras/nodes/PComposite.java b/core/src/main/java/org/piccolo2d/nodes/PComposite.java similarity index 99% rename from extras/src/main/java/org/piccolo2d/extras/nodes/PComposite.java rename to core/src/main/java/org/piccolo2d/nodes/PComposite.java index 6db824a3..a6427fd6 100644 --- a/extras/src/main/java/org/piccolo2d/extras/nodes/PComposite.java +++ b/core/src/main/java/org/piccolo2d/nodes/PComposite.java @@ -26,7 +26,7 @@ * 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.nodes; +package org.piccolo2d.nodes; import org.piccolo2d.PNode; import org.piccolo2d.util.PPickPath; diff --git a/extras/src/main/java/org/piccolo2d/extras/nodes/PLens.java b/core/src/main/java/org/piccolo2d/nodes/PLens.java similarity index 98% rename from extras/src/main/java/org/piccolo2d/extras/nodes/PLens.java rename to core/src/main/java/org/piccolo2d/nodes/PLens.java index 2ce19436..0b942e0e 100644 --- a/extras/src/main/java/org/piccolo2d/extras/nodes/PLens.java +++ b/core/src/main/java/org/piccolo2d/nodes/PLens.java @@ -26,7 +26,7 @@ * 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.nodes; +package org.piccolo2d.nodes; import java.awt.Color; import java.awt.Paint; @@ -40,7 +40,6 @@ import org.piccolo2d.PLayer; import org.piccolo2d.PNode; import org.piccolo2d.event.PDragEventHandler; -import org.piccolo2d.nodes.PPath; /** diff --git a/extras/src/main/java/org/piccolo2d/extras/nodes/PLine.java b/core/src/main/java/org/piccolo2d/nodes/PLine.java similarity index 98% rename from extras/src/main/java/org/piccolo2d/extras/nodes/PLine.java rename to core/src/main/java/org/piccolo2d/nodes/PLine.java index 05c83cc4..c8a40469 100644 --- a/extras/src/main/java/org/piccolo2d/extras/nodes/PLine.java +++ b/core/src/main/java/org/piccolo2d/nodes/PLine.java @@ -26,7 +26,7 @@ * 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.nodes; +package org.piccolo2d.nodes; import java.awt.BasicStroke; import java.awt.Color; @@ -40,8 +40,7 @@ import java.io.ObjectOutputStream; import org.piccolo2d.PNode; -import org.piccolo2d.extras.util.LineShape; -import org.piccolo2d.nodes.PPath; +import org.piccolo2d.util.LineShape; import org.piccolo2d.util.PAffineTransform; import org.piccolo2d.util.PPaintContext; import org.piccolo2d.util.PUtil; diff --git a/extras/src/main/java/org/piccolo2d/extras/nodes/PNodeCache.java b/core/src/main/java/org/piccolo2d/nodes/PNodeCache.java similarity index 99% rename from extras/src/main/java/org/piccolo2d/extras/nodes/PNodeCache.java rename to core/src/main/java/org/piccolo2d/nodes/PNodeCache.java index 11719be7..320c4eb1 100644 --- a/extras/src/main/java/org/piccolo2d/extras/nodes/PNodeCache.java +++ b/core/src/main/java/org/piccolo2d/nodes/PNodeCache.java @@ -26,7 +26,7 @@ * 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.nodes; +package org.piccolo2d.nodes; import java.awt.Graphics2D; import java.awt.Image; diff --git a/core/src/main/java/org/piccolo2d/nodes/PPath.java b/core/src/main/java/org/piccolo2d/nodes/PPath.java index db8bda06..979a755c 100644 --- a/core/src/main/java/org/piccolo2d/nodes/PPath.java +++ b/core/src/main/java/org/piccolo2d/nodes/PPath.java @@ -47,6 +47,8 @@ * Abstract path node. */ public abstract class PPath extends PShape { + + private static final long serialVersionUID = 1L; /** Path for this path node. */ private final Path2D path; @@ -78,6 +80,8 @@ private PPath(final Path2D path, final Stroke stroke) { * Path node with coordinates stored in single precision floating point. */ public static class Float extends PPath { + + private static final long serialVersionUID = 1L; /** * Create a new empty path node. @@ -142,6 +146,8 @@ public Float(final Path2D.Float path, final Stroke stroke) { * Path node with coordinates stored in double precision floating point. */ public static class Double extends PPath { + + private static final long serialVersionUID = 1L; /** * Create a new empty path node. diff --git a/extras/src/main/java/org/piccolo2d/extras/nodes/PShadow.java b/core/src/main/java/org/piccolo2d/nodes/PShadow.java similarity index 93% rename from extras/src/main/java/org/piccolo2d/extras/nodes/PShadow.java rename to core/src/main/java/org/piccolo2d/nodes/PShadow.java index 69779f78..7a148f99 100755 --- a/extras/src/main/java/org/piccolo2d/extras/nodes/PShadow.java +++ b/core/src/main/java/org/piccolo2d/nodes/PShadow.java @@ -26,13 +26,12 @@ * 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.nodes; +package org.piccolo2d.nodes; import java.awt.Image; import java.awt.Paint; -import org.piccolo2d.extras.util.ShadowUtils; -import org.piccolo2d.nodes.PImage; +import org.piccolo2d.util.ShadowUtils; diff --git a/core/src/main/java/org/piccolo2d/nodes/PShape.java b/core/src/main/java/org/piccolo2d/nodes/PShape.java index f69d46bf..bd300022 100644 --- a/core/src/main/java/org/piccolo2d/nodes/PShape.java +++ b/core/src/main/java/org/piccolo2d/nodes/PShape.java @@ -47,7 +47,9 @@ */ public abstract class PShape extends PNode { - /** Stroke for this shape node, defaults to {@link #DEFAULT_STROKE}. */ + private static final long serialVersionUID = 1L; + + /** Stroke for this shape node, defaults to {@link #DEFAULT_STROKE}. */ private transient Stroke stroke = DEFAULT_STROKE; /** Stroke paint for this shape node, defaults to {@link #DEFAULT_STROKE_PAINT}. */ diff --git a/extras/src/main/java/org/piccolo2d/extras/nodes/PStyledText.java b/core/src/main/java/org/piccolo2d/nodes/PStyledText.java similarity index 95% rename from extras/src/main/java/org/piccolo2d/extras/nodes/PStyledText.java rename to core/src/main/java/org/piccolo2d/nodes/PStyledText.java index eb8520cb..7db203ce 100644 --- a/extras/src/main/java/org/piccolo2d/extras/nodes/PStyledText.java +++ b/core/src/main/java/org/piccolo2d/nodes/PStyledText.java @@ -26,7 +26,7 @@ * 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.nodes; +package org.piccolo2d.nodes; import java.awt.Color; import java.awt.Font; @@ -78,7 +78,7 @@ public class PStyledText extends PNode { protected Document document; /** String contents of the document. */ - protected transient ArrayList stringContents; + protected transient ArrayList stringContents; /** Tracks the information about line metrics within the document. */ protected transient LineInfo[] lines; @@ -174,7 +174,7 @@ public void setDocument(final Document document) { */ public void syncWithDocument() { // First get the actual text and stick it in an Attributed String - stringContents = new ArrayList(); + stringContents = new ArrayList(); String documentString; try { @@ -188,7 +188,7 @@ public void syncWithDocument() { } // The paragraph start and end indices - final ArrayList pEnds = extractParagraphRanges(documentString); + final ArrayList pEnds = extractParagraphRanges(documentString); // The default style context - which will be reused final StyleContext styleContext = StyleContext.getDefaultStyleContext(); @@ -198,11 +198,11 @@ public void syncWithDocument() { AttributedString attributedString; - final Iterator contentIterator = stringContents.iterator(); - final Iterator paragraphIterator = pEnds.iterator(); + final Iterator contentIterator = stringContents.iterator(); + final Iterator paragraphIterator = pEnds.iterator(); while (contentIterator.hasNext() && paragraphIterator.hasNext()) { - paragraphRange = (RunInfo) paragraphIterator.next(); - attributedString = (AttributedString) contentIterator.next(); + paragraphRange = paragraphIterator.next(); + attributedString = contentIterator.next(); pos = paragraphRange.startIndex; // The current element will be used as a temp variable while @@ -356,9 +356,9 @@ private Font extractFontFromDefaultStyledDocument(final DefaultStyledDocument st return font; } - private ArrayList extractParagraphRanges(final String documentString) { + private ArrayList extractParagraphRanges(final String documentString) { // The paragraph start and end indices - final ArrayList paragraphRanges = new ArrayList(); + final ArrayList paragraphRanges = new ArrayList(); // The current position in the specified range int pos = 0; @@ -427,19 +427,19 @@ public void recomputeLayout() { return; } - final ArrayList linesList = new ArrayList(); + final ArrayList linesList = new ArrayList(); double textWidth = 0; double textHeight = 0; - final Iterator contentIterator = stringContents.iterator(); + final Iterator contentIterator = stringContents.iterator(); while (contentIterator.hasNext()) { final AttributedString ats = (AttributedString) contentIterator.next(); final AttributedCharacterIterator itr = ats.getIterator(); LineBreakMeasurer measurer; - ArrayList breakList = null; + ArrayList breakList = null; measurer = new LineBreakMeasurer(itr, SWING_FRC); breakList = extractLineBreaks(itr, measurer); @@ -468,7 +468,7 @@ public void recomputeLayout() { linesList.add(lineInfo); } - final int lineEnd = ((Integer) breakList.get(0)).intValue(); + final int lineEnd = breakList.get(0).intValue(); if (lineEnd <= itr.getRunLimit()) { breakList.remove(0); newLine = true; @@ -522,8 +522,7 @@ private double calculateLineHeightFromLineInfo(final LineInfo lineInfo) { final double lineHeight; if (lineInfo == null) { lineHeight = 0; - } - else { + } else { lineHeight = lineInfo.maxAscent + lineInfo.maxDescent + lineInfo.leading; } return lineHeight; @@ -550,18 +549,17 @@ private void constrainDimensionsIfNeeded(final double textWidth, final double te // Because swing doesn't use fractional font metrics by default, we use // LineBreakMeasurer to find out where Swing is going to break them - private ArrayList extractLineBreaks(final AttributedCharacterIterator itr, final LineBreakMeasurer measurer) { - ArrayList breakList; - breakList = new ArrayList(); + private ArrayList extractLineBreaks(final AttributedCharacterIterator itr, final LineBreakMeasurer measurer) { + ArrayList breakList = new ArrayList(); + while (measurer.getPosition() < itr.getEndIndex()) { if (constrainWidthToTextWidth) { measurer.nextLayout(Float.MAX_VALUE); - } - else { + } else { measurer.nextLayout((float) Math.ceil(getWidth() - insets.left - insets.right)); } - breakList.add(new Integer(measurer.getPosition())); + breakList.add(Integer.valueOf(measurer.getPosition())); } return breakList; } @@ -765,7 +763,7 @@ public int getEndIndex() { */ protected static class LineInfo { /** Segments which make up this line's formatting segments. */ - public List segments; + public List segments; /** Maximum of the line segments' ascents. */ public double maxAscent; @@ -780,7 +778,7 @@ protected static class LineInfo { * Creates a LineInfo that contains no segments. */ public LineInfo() { - segments = new ArrayList(); + segments = new ArrayList(); } } diff --git a/core/src/main/java/org/piccolo2d/nodes/PText.java b/core/src/main/java/org/piccolo2d/nodes/PText.java index 13d1bc2d..fe114d0f 100644 --- a/core/src/main/java/org/piccolo2d/nodes/PText.java +++ b/core/src/main/java/org/piccolo2d/nodes/PText.java @@ -425,7 +425,7 @@ public void setFont(final Font font) { * wrapped based on the bounds of this node. */ public void recomputeLayout() { - final ArrayList linesList = new ArrayList(); + final ArrayList linesList = new ArrayList(); double textWidth = 0; double textHeight = 0; diff --git a/extras/src/main/java/org/piccolo2d/extras/pswing/PComboBox.java b/core/src/main/java/org/piccolo2d/pswing/PComboBox.java similarity index 97% rename from extras/src/main/java/org/piccolo2d/extras/pswing/PComboBox.java rename to core/src/main/java/org/piccolo2d/pswing/PComboBox.java index 701bc177..f95f231e 100644 --- a/extras/src/main/java/org/piccolo2d/extras/pswing/PComboBox.java +++ b/core/src/main/java/org/piccolo2d/pswing/PComboBox.java @@ -26,7 +26,7 @@ * 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.pswing; +package org.piccolo2d.pswing; import java.awt.Rectangle; import java.awt.geom.Rectangle2D; @@ -72,10 +72,8 @@ */ public class PComboBox extends JComboBox implements Serializable { - /** - * - */ private static final long serialVersionUID = 1L; + private PSwing pSwing; private PSwingCanvas canvas; @@ -84,7 +82,7 @@ public class PComboBox extends JComboBox implements Serializable { * * @param model The ComboBoxModel from which the list will be created */ - public PComboBox(final ComboBoxModel model) { + public PComboBox(final ComboBoxModel model) { super(model); init(); } @@ -104,7 +102,7 @@ public PComboBox(final Object[] items) { * * @param items The items to populate the PComboBox list */ - public PComboBox(final Vector items) { + public PComboBox(final Vector items) { super(items); init(); } @@ -141,7 +139,7 @@ public void setEnvironment(final PSwing pSwingNode, final PSwingCanvas canvasEnv * arrowButton and the component itself and to create our PopupMenu rather * than the default. */ - protected class PBasicComboBoxUI extends BasicComboBoxUI { + public class PBasicComboBoxUI extends BasicComboBoxUI { /** * Create our Popup instead of swing's default. diff --git a/extras/src/main/java/org/piccolo2d/extras/pswing/PSwing.java b/core/src/main/java/org/piccolo2d/pswing/PSwing.java similarity index 96% rename from extras/src/main/java/org/piccolo2d/extras/pswing/PSwing.java rename to core/src/main/java/org/piccolo2d/pswing/PSwing.java index d69d09f1..060d20c0 100644 --- a/extras/src/main/java/org/piccolo2d/extras/pswing/PSwing.java +++ b/core/src/main/java/org/piccolo2d/pswing/PSwing.java @@ -26,7 +26,7 @@ * 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.pswing; +package org.piccolo2d.pswing; import org.piccolo2d.PCamera; import org.piccolo2d.PLayer; @@ -249,7 +249,7 @@ public class PSwing extends PNode implements Serializable, PropertyChangeListene * Used to keep track of which nodes we've attached listeners to since no * built in support in PNode. */ - private final ArrayList listeningTo = new ArrayList(); + private final ArrayList listeningTo = new ArrayList(); /** The parent listener for camera/canvas changes. */ private final PropertyChangeListener parentListener = new PropertyChangeListener() { @@ -328,16 +328,6 @@ public PSwing(final JComponent component) { listenForCanvas(this); } - /** - * @deprecated by {@link #PSwing(JComponent)} - * - * @param swingCanvas canvas on which the PSwing node will be embedded - * @param component not used - */ - public PSwing(final PSwingCanvas swingCanvas, final JComponent component) { - this(component); - } - /** * If true {@link PSwing} will paint the {@link JComponent} to a buffer with no graphics * transformations applied and then paint the buffer to the target transformed diff --git a/extras/src/main/java/org/piccolo2d/extras/pswing/PSwingCanvas.java b/core/src/main/java/org/piccolo2d/pswing/PSwingCanvas.java similarity index 94% rename from extras/src/main/java/org/piccolo2d/extras/pswing/PSwingCanvas.java rename to core/src/main/java/org/piccolo2d/pswing/PSwingCanvas.java index 8420fd6d..811903ad 100644 --- a/extras/src/main/java/org/piccolo2d/extras/pswing/PSwingCanvas.java +++ b/core/src/main/java/org/piccolo2d/pswing/PSwingCanvas.java @@ -26,7 +26,7 @@ * 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.pswing; +package org.piccolo2d.pswing; import java.awt.Dimension; @@ -67,7 +67,7 @@ private void initRepaintManager() { } } - JComponent getSwingWrapper() { + public JComponent getSwingWrapper() { return swingWrapper; } @@ -75,7 +75,7 @@ void addPSwing(final PSwing pSwing) { swingWrapper.add(pSwing.getComponent()); } - void removePSwing(final PSwing pSwing) { + public void removePSwing(final PSwing pSwing) { swingWrapper.remove(pSwing.getComponent()); } diff --git a/extras/src/main/java/org/piccolo2d/extras/pswing/PSwingEvent.java b/core/src/main/java/org/piccolo2d/pswing/PSwingEvent.java similarity index 96% rename from extras/src/main/java/org/piccolo2d/extras/pswing/PSwingEvent.java rename to core/src/main/java/org/piccolo2d/pswing/PSwingEvent.java index b22c10c3..afa984ef 100644 --- a/extras/src/main/java/org/piccolo2d/extras/pswing/PSwingEvent.java +++ b/core/src/main/java/org/piccolo2d/pswing/PSwingEvent.java @@ -26,7 +26,7 @@ * 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.pswing; +package org.piccolo2d.pswing; import org.piccolo2d.PNode; import org.piccolo2d.util.PPickPath; diff --git a/extras/src/main/java/org/piccolo2d/extras/pswing/PSwingEventHandler.java b/core/src/main/java/org/piccolo2d/pswing/PSwingEventHandler.java similarity index 95% rename from extras/src/main/java/org/piccolo2d/extras/pswing/PSwingEventHandler.java rename to core/src/main/java/org/piccolo2d/pswing/PSwingEventHandler.java index 307c6f10..bf6915b8 100644 --- a/extras/src/main/java/org/piccolo2d/extras/pswing/PSwingEventHandler.java +++ b/core/src/main/java/org/piccolo2d/pswing/PSwingEventHandler.java @@ -26,7 +26,7 @@ * 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.pswing; +package org.piccolo2d.pswing; import org.piccolo2d.PCamera; import org.piccolo2d.PLayer; @@ -261,7 +261,7 @@ else if (SwingUtilities.isRightMouseButton(mEvent)) { } else if (isPressOrClickOrMove(pSwingMouseEvent) && comp != null) { final MouseEvent tempEvent = new MouseEvent(comp, pSwingMouseEvent.getID(), mEvent.getWhen(), mEvent - .getModifiers(), point.x - offset.x, point.y - offset.y, mEvent.getClickCount(), mEvent + .getModifiersEx(), point.x - offset.x, point.y - offset.y, mEvent.getClickCount(), mEvent .isPopupTrigger()); final PSwingEvent e2 = PSwingMouseEvent.createMouseEvent(tempEvent.getID(), tempEvent, aEvent); @@ -271,7 +271,7 @@ else if (isWheelEvent(pSwingMouseEvent) && comp != null) { final MouseWheelEvent mWEvent = (MouseWheelEvent) mEvent; final MouseWheelEvent tempEvent = new MouseWheelEvent(comp, pSwingMouseEvent.getID(), mEvent.getWhen(), - mEvent.getModifiers(), point.x - offset.x, point.y - offset.y, mEvent.getClickCount(), mEvent + mEvent.getModifiersEx(), point.x - offset.x, point.y - offset.y, mEvent.getClickCount(), mEvent .isPopupTrigger(), mWEvent.getScrollType(), mWEvent.getScrollAmount(), mWEvent .getWheelRotation()); @@ -381,7 +381,7 @@ private void handleButton(final PSwingEvent e1, final PInputEvent aEvent, final final Point2D pt = new Point2D.Double(m1.getX(), m1.getY()); cameraToLocal(e1.getPath().getTopCamera(), pt, buttonData.getPNode()); final MouseEvent tempEvent = new MouseEvent(buttonData.getFocusedComponent(), m1.getID(), m1.getWhen(), m1 - .getModifiers(), (int) pt.getX() - buttonData.getOffsetX(), (int) pt.getY() + .getModifiersEx(), (int) pt.getX() - buttonData.getOffsetX(), (int) pt.getY() - buttonData.getOffsetY(), m1.getClickCount(), m1.isPopupTrigger()); final PSwingEvent e2 = PSwingMouseEvent.createMouseEvent(tempEvent.getID(), tempEvent, aEvent); diff --git a/extras/src/main/java/org/piccolo2d/extras/pswing/PSwingMouseEvent.java b/core/src/main/java/org/piccolo2d/pswing/PSwingMouseEvent.java similarity index 99% rename from extras/src/main/java/org/piccolo2d/extras/pswing/PSwingMouseEvent.java rename to core/src/main/java/org/piccolo2d/pswing/PSwingMouseEvent.java index 50b2fa85..93f5acf7 100644 --- a/extras/src/main/java/org/piccolo2d/extras/pswing/PSwingMouseEvent.java +++ b/core/src/main/java/org/piccolo2d/pswing/PSwingMouseEvent.java @@ -26,7 +26,7 @@ * 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.pswing; +package org.piccolo2d.pswing; import java.awt.Component; import java.awt.event.MouseEvent; @@ -89,7 +89,7 @@ public class PSwingMouseEvent extends MouseEvent implements Serializable, PSwing * @param piccoloEvent used to query about the event's Piccolo context */ protected PSwingMouseEvent(final int id, final MouseEvent swingEvent, final PInputEvent piccoloEvent) { - super((Component) swingEvent.getSource(), swingEvent.getID(), swingEvent.getWhen(), swingEvent.getModifiers(), + super((Component) swingEvent.getSource(), swingEvent.getID(), swingEvent.getWhen(), swingEvent.getModifiersEx(), swingEvent.getX(), swingEvent.getY(), swingEvent.getClickCount(), swingEvent.isPopupTrigger()); this.id = id; this.event = piccoloEvent; diff --git a/extras/src/main/java/org/piccolo2d/extras/pswing/PSwingMouseMotionEvent.java b/core/src/main/java/org/piccolo2d/pswing/PSwingMouseMotionEvent.java similarity index 99% rename from extras/src/main/java/org/piccolo2d/extras/pswing/PSwingMouseMotionEvent.java rename to core/src/main/java/org/piccolo2d/pswing/PSwingMouseMotionEvent.java index a3ce9f2e..85688653 100644 --- a/extras/src/main/java/org/piccolo2d/extras/pswing/PSwingMouseMotionEvent.java +++ b/core/src/main/java/org/piccolo2d/pswing/PSwingMouseMotionEvent.java @@ -26,7 +26,7 @@ * 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.pswing; +package org.piccolo2d.pswing; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionListener; diff --git a/extras/src/main/java/org/piccolo2d/extras/pswing/PSwingMouseWheelEvent.java b/core/src/main/java/org/piccolo2d/pswing/PSwingMouseWheelEvent.java similarity index 96% rename from extras/src/main/java/org/piccolo2d/extras/pswing/PSwingMouseWheelEvent.java rename to core/src/main/java/org/piccolo2d/pswing/PSwingMouseWheelEvent.java index 2f15f820..4489149c 100644 --- a/extras/src/main/java/org/piccolo2d/extras/pswing/PSwingMouseWheelEvent.java +++ b/core/src/main/java/org/piccolo2d/pswing/PSwingMouseWheelEvent.java @@ -26,7 +26,7 @@ * 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.pswing; +package org.piccolo2d.pswing; import org.piccolo2d.PNode; import org.piccolo2d.event.PInputEvent; @@ -86,7 +86,7 @@ public class PSwingMouseWheelEvent extends MouseWheelEvent implements PSwingEven * event's piccolo2d context */ protected PSwingMouseWheelEvent(final int id, final MouseWheelEvent swingEvent, final PInputEvent piccoloEvent) { - super((Component) swingEvent.getSource(), swingEvent.getID(), swingEvent.getWhen(), swingEvent.getModifiers(), + super((Component) swingEvent.getSource(), swingEvent.getID(), swingEvent.getWhen(), swingEvent.getModifiersEx(), swingEvent.getX(), swingEvent.getY(), swingEvent.getClickCount(), swingEvent.isPopupTrigger(), swingEvent.getScrollType(), swingEvent.getScrollAmount(), swingEvent.getWheelRotation()); this.id = id; diff --git a/extras/src/main/java/org/piccolo2d/extras/pswing/PSwingRepaintManager.java b/core/src/main/java/org/piccolo2d/pswing/PSwingRepaintManager.java similarity index 98% rename from extras/src/main/java/org/piccolo2d/extras/pswing/PSwingRepaintManager.java rename to core/src/main/java/org/piccolo2d/pswing/PSwingRepaintManager.java index cea6c688..a8e794a2 100644 --- a/extras/src/main/java/org/piccolo2d/extras/pswing/PSwingRepaintManager.java +++ b/core/src/main/java/org/piccolo2d/pswing/PSwingRepaintManager.java @@ -26,7 +26,7 @@ * 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.pswing; +package org.piccolo2d.pswing; import java.awt.Component; import java.util.Vector; @@ -75,7 +75,7 @@ public class PSwingRepaintManager extends RepaintManager { // The components that are currently painting // This needs to be a vector for thread safety - private final Vector paintingComponents = new Vector(); + private final Vector paintingComponents = new Vector(); /** * Locks repaint for a particular (Swing) component displayed by PCanvas. diff --git a/extras/src/main/java/org/piccolo2d/extras/pswing/package.html b/core/src/main/java/org/piccolo2d/pswing/package.html similarity index 100% rename from extras/src/main/java/org/piccolo2d/extras/pswing/package.html rename to core/src/main/java/org/piccolo2d/pswing/package.html diff --git a/extras/src/main/java/org/piccolo2d/extras/pswing/readme.txt b/core/src/main/java/org/piccolo2d/pswing/readme.txt similarity index 100% rename from extras/src/main/java/org/piccolo2d/extras/pswing/readme.txt rename to core/src/main/java/org/piccolo2d/pswing/readme.txt diff --git a/extras/src/main/java/org/piccolo2d/extras/swing/PCacheCanvas.java b/core/src/main/java/org/piccolo2d/swing/PCacheCanvas.java similarity index 96% rename from extras/src/main/java/org/piccolo2d/extras/swing/PCacheCanvas.java rename to core/src/main/java/org/piccolo2d/swing/PCacheCanvas.java index 8be29ebf..c4308857 100644 --- a/extras/src/main/java/org/piccolo2d/extras/swing/PCacheCanvas.java +++ b/core/src/main/java/org/piccolo2d/swing/PCacheCanvas.java @@ -26,13 +26,13 @@ * 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.swing; +package org.piccolo2d.swing; import org.piccolo2d.PCamera; import org.piccolo2d.PCanvas; import org.piccolo2d.PLayer; import org.piccolo2d.PRoot; -import org.piccolo2d.extras.nodes.PCacheCamera; +import org.piccolo2d.nodes.PCacheCamera; /** diff --git a/extras/src/main/java/org/piccolo2d/extras/swing/PDefaultScrollDirector.java b/core/src/main/java/org/piccolo2d/swing/PDefaultScrollDirector.java similarity index 93% rename from extras/src/main/java/org/piccolo2d/extras/swing/PDefaultScrollDirector.java rename to core/src/main/java/org/piccolo2d/swing/PDefaultScrollDirector.java index ab6cc287..a764ccf1 100644 --- a/extras/src/main/java/org/piccolo2d/extras/swing/PDefaultScrollDirector.java +++ b/core/src/main/java/org/piccolo2d/swing/PDefaultScrollDirector.java @@ -26,7 +26,7 @@ * 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.swing; +package org.piccolo2d.swing; import java.awt.Dimension; import java.awt.Point; @@ -139,8 +139,8 @@ public Point getViewPosition(final Rectangle2D viewBounds) { if (camera != null) { // First we compute the union of all the layers final PBounds layerBounds = new PBounds(); - final List layers = camera.getLayersReference(); - for (final Iterator i = layers.iterator(); i.hasNext();) { + final List layers = camera.getLayersReference(); + for (final Iterator i = layers.iterator(); i.hasNext();) { final PLayer layer = (PLayer) i.next(); layerBounds.add(layer.getFullBoundsReference()); } @@ -169,9 +169,9 @@ public Dimension getViewSize(final Rectangle2D viewBounds) { if (camera != null) { // First we compute the union of all the layers final PBounds bounds = new PBounds(); - final List layers = camera.getLayersReference(); - for (final Iterator i = layers.iterator(); i.hasNext();) { - final PLayer layer = (PLayer) i.next(); + final List layers = camera.getLayersReference(); + for (final Iterator i = layers.iterator(); i.hasNext();) { + final PLayer layer = i.next(); bounds.add(layer.getFullBoundsReference()); } @@ -205,9 +205,9 @@ public void setViewPosition(final double x, final double y) { // Get the union of all the layers' bounds final PBounds layerBounds = new PBounds(); - final List layers = camera.getLayersReference(); - for (final Iterator i = layers.iterator(); i.hasNext();) { - final PLayer layer = (PLayer) i.next(); + final List layers = camera.getLayersReference(); + for (final Iterator i = layers.iterator(); i.hasNext();) { + final PLayer layer = i.next(); layerBounds.add(layer.getFullBoundsReference()); } @@ -277,9 +277,9 @@ public boolean shouldRevalidateScrollPane() { // Get the union of all the layers' bounds final PBounds layerBounds = new PBounds(); - final List layers = camera.getLayersReference(); - for (final Iterator i = layers.iterator(); i.hasNext();) { - final PLayer layer = (PLayer) i.next(); + final List layers = camera.getLayersReference(); + for (final Iterator i = layers.iterator(); i.hasNext();) { + final PLayer layer = i.next(); layerBounds.add(layer.getFullBoundsReference()); } diff --git a/extras/src/main/java/org/piccolo2d/extras/swing/PScrollDirector.java b/core/src/main/java/org/piccolo2d/swing/PScrollDirector.java similarity index 98% rename from extras/src/main/java/org/piccolo2d/extras/swing/PScrollDirector.java rename to core/src/main/java/org/piccolo2d/swing/PScrollDirector.java index 16776373..3274e9c3 100644 --- a/extras/src/main/java/org/piccolo2d/extras/swing/PScrollDirector.java +++ b/core/src/main/java/org/piccolo2d/swing/PScrollDirector.java @@ -26,7 +26,7 @@ * 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.swing; +package org.piccolo2d.swing; import java.awt.Dimension; import java.awt.Point; diff --git a/extras/src/main/java/org/piccolo2d/extras/swing/PScrollPane.java b/core/src/main/java/org/piccolo2d/swing/PScrollPane.java similarity index 99% rename from extras/src/main/java/org/piccolo2d/extras/swing/PScrollPane.java rename to core/src/main/java/org/piccolo2d/swing/PScrollPane.java index 6d927c70..c2b94155 100644 --- a/extras/src/main/java/org/piccolo2d/extras/swing/PScrollPane.java +++ b/core/src/main/java/org/piccolo2d/swing/PScrollPane.java @@ -26,7 +26,7 @@ * 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.swing; +package org.piccolo2d.swing; import java.awt.Component; import java.awt.Dimension; diff --git a/extras/src/main/java/org/piccolo2d/extras/swing/PScrollPaneLayout.java b/core/src/main/java/org/piccolo2d/swing/PScrollPaneLayout.java similarity index 99% rename from extras/src/main/java/org/piccolo2d/extras/swing/PScrollPaneLayout.java rename to core/src/main/java/org/piccolo2d/swing/PScrollPaneLayout.java index 2b22558a..accaf6e8 100644 --- a/extras/src/main/java/org/piccolo2d/extras/swing/PScrollPaneLayout.java +++ b/core/src/main/java/org/piccolo2d/swing/PScrollPaneLayout.java @@ -26,7 +26,7 @@ * 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.swing; +package org.piccolo2d.swing; import java.awt.Container; import java.awt.Dimension; diff --git a/extras/src/main/java/org/piccolo2d/extras/swing/PViewport.java b/core/src/main/java/org/piccolo2d/swing/PViewport.java similarity index 99% rename from extras/src/main/java/org/piccolo2d/extras/swing/PViewport.java rename to core/src/main/java/org/piccolo2d/swing/PViewport.java index 85f1599a..29d388ae 100644 --- a/extras/src/main/java/org/piccolo2d/extras/swing/PViewport.java +++ b/core/src/main/java/org/piccolo2d/swing/PViewport.java @@ -26,7 +26,7 @@ * 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.swing; +package org.piccolo2d.swing; import java.awt.Component; import java.awt.Container; diff --git a/extras/src/main/java/org/piccolo2d/extras/swing/SwingLayoutNode.java b/core/src/main/java/org/piccolo2d/swing/SwingLayoutNode.java similarity index 95% rename from extras/src/main/java/org/piccolo2d/extras/swing/SwingLayoutNode.java rename to core/src/main/java/org/piccolo2d/swing/SwingLayoutNode.java index 3c81cf1a..abfba14f 100644 --- a/extras/src/main/java/org/piccolo2d/extras/swing/SwingLayoutNode.java +++ b/core/src/main/java/org/piccolo2d/swing/SwingLayoutNode.java @@ -26,7 +26,7 @@ * 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.swing; +package org.piccolo2d.swing; import java.awt.Component; import java.awt.Container; @@ -254,16 +254,16 @@ public void addChild(final PNode child, final Anchor anchor) { * the child * @param anchor specifies the location from which layout takes place */ - public void addChildren(final Collection nodes, final Object constraints, final Anchor anchor) { - final Iterator i = nodes.iterator(); + public void addChildren(final Collection nodes, final Object constraints, final Anchor anchor) { + final Iterator i = nodes.iterator(); while (i.hasNext()) { - final PNode each = (PNode) i.next(); + final PNode each = i.next(); addChild(each, constraints, anchor); } } /** {@inheritDoc} */ - public void addChildren(final Collection nodes) { + public void addChildren(final Collection nodes) { addChildren(nodes, null, defaultAnchor); } @@ -274,7 +274,7 @@ public void addChildren(final Collection nodes) { * @param constraints constraints the layout manager uses when laying out * the child */ - public void addChildren(final Collection nodes, final Object constraints) { + public void addChildren(final Collection nodes, final Object constraints) { addChildren(nodes, constraints, defaultAnchor); } @@ -284,7 +284,7 @@ public void addChildren(final Collection nodes, final Object constraints) { * @param nodes nodes to add to the end of the list * @param anchor specifies the location from which layout takes place */ - public void addChildren(final Collection nodes, final Anchor anchor) { + public void addChildren(final Collection nodes, final Anchor anchor) { addChildren(nodes, null, anchor); } @@ -318,9 +318,9 @@ public PNode removeChild(final int index) { * less efficient) manner. */ public void removeAllChildren() { - final Iterator i = getChildrenIterator(); + final Iterator i = getChildrenIterator(); while (i.hasNext()) { - removeChild((PNode) i.next()); + removeChild(i.next()); } } diff --git a/extras/src/main/java/org/piccolo2d/extras/swing/package.html b/core/src/main/java/org/piccolo2d/swing/package.html similarity index 100% rename from extras/src/main/java/org/piccolo2d/extras/swing/package.html rename to core/src/main/java/org/piccolo2d/swing/package.html diff --git a/extras/src/main/java/org/piccolo2d/extras/util/LineShape.java b/core/src/main/java/org/piccolo2d/util/LineShape.java similarity index 99% rename from extras/src/main/java/org/piccolo2d/extras/util/LineShape.java rename to core/src/main/java/org/piccolo2d/util/LineShape.java index 9f380545..56aa4619 100644 --- a/extras/src/main/java/org/piccolo2d/extras/util/LineShape.java +++ b/core/src/main/java/org/piccolo2d/util/LineShape.java @@ -26,7 +26,7 @@ * 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.util; +package org.piccolo2d.util; import java.awt.Rectangle; import java.awt.Shape; diff --git a/extras/src/main/java/org/piccolo2d/extras/util/MutablePoints.java b/core/src/main/java/org/piccolo2d/util/MutablePoints.java similarity index 98% rename from extras/src/main/java/org/piccolo2d/extras/util/MutablePoints.java rename to core/src/main/java/org/piccolo2d/util/MutablePoints.java index bad1012d..bbcb38a0 100644 --- a/extras/src/main/java/org/piccolo2d/extras/util/MutablePoints.java +++ b/core/src/main/java/org/piccolo2d/util/MutablePoints.java @@ -26,7 +26,7 @@ * 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.util; +package org.piccolo2d.util; import java.awt.geom.AffineTransform; diff --git a/extras/src/main/java/org/piccolo2d/extras/util/PBoundsLocator.java b/core/src/main/java/org/piccolo2d/util/PBoundsLocator.java similarity index 99% rename from extras/src/main/java/org/piccolo2d/extras/util/PBoundsLocator.java rename to core/src/main/java/org/piccolo2d/util/PBoundsLocator.java index 02511088..fa972eb6 100644 --- a/extras/src/main/java/org/piccolo2d/extras/util/PBoundsLocator.java +++ b/core/src/main/java/org/piccolo2d/util/PBoundsLocator.java @@ -26,7 +26,7 @@ * 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.util; +package org.piccolo2d.util; import java.awt.geom.Rectangle2D; diff --git a/extras/src/main/java/org/piccolo2d/extras/util/PFixedWidthStroke.java b/core/src/main/java/org/piccolo2d/util/PFixedWidthStroke.java similarity index 99% rename from extras/src/main/java/org/piccolo2d/extras/util/PFixedWidthStroke.java rename to core/src/main/java/org/piccolo2d/util/PFixedWidthStroke.java index 6ae6175c..f3cc202f 100644 --- a/extras/src/main/java/org/piccolo2d/extras/util/PFixedWidthStroke.java +++ b/core/src/main/java/org/piccolo2d/util/PFixedWidthStroke.java @@ -26,7 +26,7 @@ * 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.util; +package org.piccolo2d.util; import java.awt.BasicStroke; import java.awt.Stroke; diff --git a/extras/src/main/java/org/piccolo2d/extras/util/PLocator.java b/core/src/main/java/org/piccolo2d/util/PLocator.java similarity index 98% rename from extras/src/main/java/org/piccolo2d/extras/util/PLocator.java rename to core/src/main/java/org/piccolo2d/util/PLocator.java index 82fab19b..3d276cfe 100644 --- a/extras/src/main/java/org/piccolo2d/extras/util/PLocator.java +++ b/core/src/main/java/org/piccolo2d/util/PLocator.java @@ -26,7 +26,7 @@ * 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.util; +package org.piccolo2d.util; import java.awt.geom.Point2D; import java.io.Serializable; diff --git a/extras/src/main/java/org/piccolo2d/extras/util/PNodeLocator.java b/core/src/main/java/org/piccolo2d/util/PNodeLocator.java similarity index 98% rename from extras/src/main/java/org/piccolo2d/extras/util/PNodeLocator.java rename to core/src/main/java/org/piccolo2d/util/PNodeLocator.java index 96da4493..dd35a8bf 100644 --- a/extras/src/main/java/org/piccolo2d/extras/util/PNodeLocator.java +++ b/core/src/main/java/org/piccolo2d/util/PNodeLocator.java @@ -26,7 +26,7 @@ * 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.util; +package org.piccolo2d.util; import org.piccolo2d.PNode; diff --git a/core/src/main/java/org/piccolo2d/util/PObjectOutputStream.java b/core/src/main/java/org/piccolo2d/util/PObjectOutputStream.java index 350c31e1..5c9f9324 100644 --- a/core/src/main/java/org/piccolo2d/util/PObjectOutputStream.java +++ b/core/src/main/java/org/piccolo2d/util/PObjectOutputStream.java @@ -68,7 +68,7 @@ public class PObjectOutputStream extends ObjectOutputStream { private boolean writingRoot; - private final HashMap unconditionallyWritten; + private final HashMap unconditionallyWritten; /** * Transform the given object into an array of bytes. @@ -94,7 +94,7 @@ public static byte[] toByteArray(final Object object) throws IOException { */ public PObjectOutputStream(final OutputStream out) throws IOException { super(out); - unconditionallyWritten = new HashMap(); + unconditionallyWritten = new HashMap(); } /** @@ -165,7 +165,9 @@ public Object replaceObject(final Object object) { public void writeConditionalObject(final Object object) throws IOException { } } - new ZMarkObjectOutputStream().writeObject(aRoot); + ZMarkObjectOutputStream zmoos = new ZMarkObjectOutputStream(); + zmoos.writeObject(aRoot); + zmoos.close(); } private static final OutputStream NULL_OUTPUT_STREAM = new OutputStream() { diff --git a/extras/src/main/java/org/piccolo2d/extras/util/POcclusionDetection.java b/core/src/main/java/org/piccolo2d/util/POcclusionDetection.java similarity index 97% rename from extras/src/main/java/org/piccolo2d/extras/util/POcclusionDetection.java rename to core/src/main/java/org/piccolo2d/util/POcclusionDetection.java index 7b732d4a..3502e6e7 100644 --- a/extras/src/main/java/org/piccolo2d/extras/util/POcclusionDetection.java +++ b/core/src/main/java/org/piccolo2d/util/POcclusionDetection.java @@ -26,11 +26,9 @@ * 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.util; +package org.piccolo2d.util; import org.piccolo2d.PNode; -import org.piccolo2d.util.PBounds; -import org.piccolo2d.util.PPickPath; /** * Experimental class for detecting occlusions. diff --git a/core/src/main/java/org/piccolo2d/util/PPickPath.java b/core/src/main/java/org/piccolo2d/util/PPickPath.java index 8ee5e247..8bf3c343 100644 --- a/core/src/main/java/org/piccolo2d/util/PPickPath.java +++ b/core/src/main/java/org/piccolo2d/util/PPickPath.java @@ -77,7 +77,7 @@ public class PPickPath implements PInputEventListener { private PStack transformStack; private PStack pickBoundsStack; private PCamera bottomCamera; - private HashMap excludedNodes; + private HashMap excludedNodes; /** * Creates a pick pack originating from the provided camera and with the @@ -169,7 +169,7 @@ public PNode nextPickedNode() { return null; } if (excludedNodes == null) { - excludedNodes = new HashMap(); + excludedNodes = new HashMap(); } // exclude current picked node diff --git a/extras/src/main/java/org/piccolo2d/extras/util/PSemanticStroke.java b/core/src/main/java/org/piccolo2d/util/PSemanticStroke.java similarity index 98% rename from extras/src/main/java/org/piccolo2d/extras/util/PSemanticStroke.java rename to core/src/main/java/org/piccolo2d/util/PSemanticStroke.java index 2df31d24..fc22a71a 100644 --- a/extras/src/main/java/org/piccolo2d/extras/util/PSemanticStroke.java +++ b/core/src/main/java/org/piccolo2d/util/PSemanticStroke.java @@ -25,13 +25,11 @@ * 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.util; +package org.piccolo2d.util; import java.awt.Shape; import java.awt.Stroke; -import org.piccolo2d.util.PPickPath; - /** * diff --git a/core/src/main/java/org/piccolo2d/util/PStack.java b/core/src/main/java/org/piccolo2d/util/PStack.java index 12b40996..55ddd7b8 100644 --- a/core/src/main/java/org/piccolo2d/util/PStack.java +++ b/core/src/main/java/org/piccolo2d/util/PStack.java @@ -38,7 +38,7 @@ * @version 1.0 * @author Jesse Grosjean */ -public class PStack extends ArrayList { +public class PStack extends ArrayList { /** * Allows for future serialization code to understand versioned binary * formats. diff --git a/core/src/main/java/org/piccolo2d/util/PUtil.java b/core/src/main/java/org/piccolo2d/util/PUtil.java index a9eb8fa5..2b8a9671 100644 --- a/core/src/main/java/org/piccolo2d/util/PUtil.java +++ b/core/src/main/java/org/piccolo2d/util/PUtil.java @@ -63,7 +63,7 @@ public class PUtil { public static final int ACTIVITY_SCHEDULER_FRAME_DELAY = 10; /** An iterator that iterates over an empty collection. */ - public static final Iterator NULL_ITERATOR = Collections.EMPTY_LIST.iterator(); + public static final Iterator NULL_ITERATOR = Collections.EMPTY_LIST.iterator(); /** * Used when persisting paths to an object stream. Used to mark the end of @@ -72,7 +72,7 @@ public class PUtil { private static final int PATH_TERMINATOR = -1; /** A utility enumeration with no elements. */ - public static final Enumeration NULL_ENUMERATION = new Enumeration() { + public static final Enumeration NULL_ENUMERATION = new Enumeration() { public boolean hasMoreElements() { return false; } diff --git a/extras/src/main/java/org/piccolo2d/extras/util/Points.java b/core/src/main/java/org/piccolo2d/util/Points.java similarity index 98% rename from extras/src/main/java/org/piccolo2d/extras/util/Points.java rename to core/src/main/java/org/piccolo2d/util/Points.java index d903e8a1..42613991 100644 --- a/extras/src/main/java/org/piccolo2d/extras/util/Points.java +++ b/core/src/main/java/org/piccolo2d/util/Points.java @@ -26,7 +26,7 @@ * 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.util; +package org.piccolo2d.util; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; diff --git a/extras/src/main/java/org/piccolo2d/extras/util/ShadowUtils.java b/core/src/main/java/org/piccolo2d/util/ShadowUtils.java similarity index 96% rename from extras/src/main/java/org/piccolo2d/extras/util/ShadowUtils.java rename to core/src/main/java/org/piccolo2d/util/ShadowUtils.java index d36e6e89..a9330dc2 100755 --- a/extras/src/main/java/org/piccolo2d/extras/util/ShadowUtils.java +++ b/core/src/main/java/org/piccolo2d/util/ShadowUtils.java @@ -26,7 +26,7 @@ * 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.util; +package org.piccolo2d.util; import java.awt.AlphaComposite; import java.awt.Graphics2D; diff --git a/extras/src/main/java/org/piccolo2d/extras/util/XYArray.java b/core/src/main/java/org/piccolo2d/util/XYArray.java similarity index 99% rename from extras/src/main/java/org/piccolo2d/extras/util/XYArray.java rename to core/src/main/java/org/piccolo2d/util/XYArray.java index b2be9ce9..9f34bd38 100644 --- a/extras/src/main/java/org/piccolo2d/extras/util/XYArray.java +++ b/core/src/main/java/org/piccolo2d/util/XYArray.java @@ -26,7 +26,7 @@ * 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.util; +package org.piccolo2d.util; import java.awt.geom.AffineTransform; import java.awt.geom.Point2D; diff --git a/core/src/test/java/org/piccolo2d/MockPCamera.java b/core/src/test/java/org/piccolo2d/MockPCamera.java index bcfa5b62..8215a047 100644 --- a/core/src/test/java/org/piccolo2d/MockPCamera.java +++ b/core/src/test/java/org/piccolo2d/MockPCamera.java @@ -31,9 +31,6 @@ import java.util.ArrayList; import java.util.List; -import org.piccolo2d.PCamera; -import org.piccolo2d.PLayer; -import org.piccolo2d.PNode; import org.piccolo2d.util.PBounds; @@ -42,7 +39,7 @@ */ class MockPCamera extends PCamera { private static final long serialVersionUID = 1L; - private final List notifications = new ArrayList(); + private final List notifications = new ArrayList(); public void repaintFromLayer(final PBounds bounds, final PLayer layer) { notifications.add(new Notification("repaintFromLayer", bounds, layer)); @@ -68,6 +65,10 @@ public PNode getLayer() { public PBounds getBounds() { return bounds; } + + public String getType() { + return type; + } } public int getNotificationCount() { diff --git a/core/src/test/java/org/piccolo2d/MockPInputEventListener.java b/core/src/test/java/org/piccolo2d/MockPInputEventListener.java index 178a6d99..d67bc258 100644 --- a/core/src/test/java/org/piccolo2d/MockPInputEventListener.java +++ b/core/src/test/java/org/piccolo2d/MockPInputEventListener.java @@ -49,7 +49,7 @@ public Notification(final PInputEvent event, final int type) { } } - private final List notifications = new ArrayList(); + private final List notifications = new ArrayList(); public void processEvent(final PInputEvent aEvent, final int type) { notifications.add(new Notification(aEvent, type)); diff --git a/core/src/test/java/org/piccolo2d/MockPropertyChangeListener.java b/core/src/test/java/org/piccolo2d/MockPropertyChangeListener.java index 453aafb6..a552659c 100644 --- a/core/src/test/java/org/piccolo2d/MockPropertyChangeListener.java +++ b/core/src/test/java/org/piccolo2d/MockPropertyChangeListener.java @@ -37,7 +37,7 @@ * Mock PropertyChangeListener. */ public class MockPropertyChangeListener implements PropertyChangeListener { - private final List changes = new ArrayList(); + private final List changes = new ArrayList(); public void propertyChange(final PropertyChangeEvent evt) { changes.add(evt); diff --git a/extras/src/test/java/org/piccolo2d/extras/PAppletTest.java b/core/src/test/java/org/piccolo2d/PAppletTest.java similarity index 94% rename from extras/src/test/java/org/piccolo2d/extras/PAppletTest.java rename to core/src/test/java/org/piccolo2d/PAppletTest.java index 7fee89d5..123db425 100644 --- a/extras/src/test/java/org/piccolo2d/extras/PAppletTest.java +++ b/core/src/test/java/org/piccolo2d/PAppletTest.java @@ -26,10 +26,10 @@ * 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; +package org.piccolo2d; +import org.piccolo2d.PApplet; import org.piccolo2d.PCanvas; -import org.piccolo2d.extras.PApplet; import junit.framework.TestCase; diff --git a/core/src/test/java/org/piccolo2d/PCameraTest.java b/core/src/test/java/org/piccolo2d/PCameraTest.java index b7cc29ff..aabbd14c 100644 --- a/core/src/test/java/org/piccolo2d/PCameraTest.java +++ b/core/src/test/java/org/piccolo2d/PCameraTest.java @@ -40,11 +40,6 @@ import java.io.IOException; import java.util.Collection; -import org.piccolo2d.PCamera; -import org.piccolo2d.PCanvas; -import org.piccolo2d.PComponent; -import org.piccolo2d.PLayer; -import org.piccolo2d.PNode; import org.piccolo2d.activities.PActivity; import org.piccolo2d.activities.PTransformActivity; import org.piccolo2d.util.PAffineTransform; @@ -82,7 +77,7 @@ public void testClone() throws CloneNotSupportedException { camera1.addLayer(layer2); - final PCamera cameraCopy = (PCamera) camera1.clone(); + //final PCamera cameraCopy = (PCamera) camera1.clone(); //TODO: assertEquals(2, cameraCopy.getLayerCount()); } @@ -690,7 +685,7 @@ public void fullPaint(final PPaintContext paintContext) { } /** {@inheritDoc} */ - public Collection getAllNodes(final PNodeFilter filter, final Collection nodes) { + public Collection getAllNodes(final PNodeFilter filter, final Collection nodes) { getAllNodesCalled = true; return super.getAllNodes(filter, nodes); } diff --git a/extras/src/test/java/org/piccolo2d/extras/PFrameTest.java b/core/src/test/java/org/piccolo2d/PFrameTest.java similarity index 86% rename from extras/src/test/java/org/piccolo2d/extras/PFrameTest.java rename to core/src/test/java/org/piccolo2d/PFrameTest.java index 8503b124..85cc651e 100644 --- a/extras/src/test/java/org/piccolo2d/extras/PFrameTest.java +++ b/core/src/test/java/org/piccolo2d/PFrameTest.java @@ -26,12 +26,10 @@ * 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; - -import java.awt.event.KeyListener; +package org.piccolo2d; import org.piccolo2d.PCanvas; -import org.piccolo2d.extras.PFrame; +import org.piccolo2d.PFrame; import junit.framework.TestCase; @@ -40,11 +38,13 @@ * Unit test for PFrame. */ public class PFrameTest extends TestCase { - private PFrame frame; - + public void testCanvasIsValidWithDefaultConstructor() { PFrame frame = new PFrame() { - public void setVisible(boolean visible) { + + private static final long serialVersionUID = 1L; + + public void setVisible(boolean visible) { // why oh why is PFrame visible by default } }; @@ -53,11 +53,16 @@ public void setVisible(boolean visible) { assertNotNull(canvas.getLayer()); assertNotNull(canvas.getCamera()); assertSame(canvas.getLayer(), canvas.getCamera().getLayer(0)); + + assertFalse(frame.isVisible()); } public void testDefaultsToWindowed() { PFrame frame = new PFrame() { - public void setVisible(boolean visible) { + + private static final long serialVersionUID = 1L; + + public void setVisible(boolean visible) { // why oh why is PFrame visible by default } }; diff --git a/core/src/test/java/org/piccolo2d/PInputManagerTest.java b/core/src/test/java/org/piccolo2d/PInputManagerTest.java index 790d848c..7b07f98a 100644 --- a/core/src/test/java/org/piccolo2d/PInputManagerTest.java +++ b/core/src/test/java/org/piccolo2d/PInputManagerTest.java @@ -32,8 +32,6 @@ import java.awt.event.KeyEvent; import java.awt.geom.Point2D; -import org.piccolo2d.PCamera; -import org.piccolo2d.PInputManager; import org.piccolo2d.event.PInputEvent; import org.piccolo2d.util.PBounds; import org.piccolo2d.util.PPickPath; diff --git a/core/src/test/java/org/piccolo2d/PLayerTest.java b/core/src/test/java/org/piccolo2d/PLayerTest.java index 6956f71c..68d49393 100644 --- a/core/src/test/java/org/piccolo2d/PLayerTest.java +++ b/core/src/test/java/org/piccolo2d/PLayerTest.java @@ -30,8 +30,6 @@ import java.util.Collection; -import org.piccolo2d.PCamera; -import org.piccolo2d.PLayer; import org.piccolo2d.util.PBounds; import junit.framework.TestCase; @@ -47,7 +45,7 @@ public void setUp() { } public void testLayerHasEmptyCamerasCollectionByDefault() { - final Collection cameras = layer.getCamerasReference(); + final Collection cameras = layer.getCamerasReference(); assertNotNull(cameras); assertTrue(cameras.isEmpty()); assertEquals(0, layer.getCameraCount()); diff --git a/core/src/test/java/org/piccolo2d/PNodeTest.java b/core/src/test/java/org/piccolo2d/PNodeTest.java index 811abc9f..b7c601fb 100644 --- a/core/src/test/java/org/piccolo2d/PNodeTest.java +++ b/core/src/test/java/org/piccolo2d/PNodeTest.java @@ -46,14 +46,13 @@ import javax.swing.text.MutableAttributeSet; -import org.piccolo2d.PCanvas; -import org.piccolo2d.PLayer; -import org.piccolo2d.PNode; +import org.junit.Before; +import org.junit.Test; import org.piccolo2d.activities.PActivity; import org.piccolo2d.activities.PColorActivity; +import org.piccolo2d.activities.PColorActivity.Target; import org.piccolo2d.activities.PInterpolatingActivity; import org.piccolo2d.activities.PTransformActivity; -import org.piccolo2d.activities.PColorActivity.Target; import org.piccolo2d.event.PBasicInputEventHandler; import org.piccolo2d.util.PAffineTransform; import org.piccolo2d.util.PAffineTransformException; @@ -78,6 +77,7 @@ public PNodeTest(final String name) { super(name); } + @Before public void setUp() { node = new PNode(); mockListener = new MockPropertyChangeListener(); @@ -90,6 +90,7 @@ public void testCenterBaseBoundsOnPoint() { assertEquals(-40, node.getBoundsReference().getY(), 0); } + @Test public void testClientProperties() { final PNode n = new PNode(); @@ -148,13 +149,13 @@ public void testFindIntersectingNodes() { c.setBounds(0, 0, 100, 100); c.scale(200); - ArrayList found = new ArrayList(); + ArrayList found = new ArrayList(); final Rectangle2D rect2d = new Rectangle2D.Double(50, 50, 10, 10); n.findIntersectingNodes(rect2d, found); assertEquals(found.size(), 2); assertEquals(rect2d.getHeight(), 10, 0); - found = new ArrayList(); + found = new ArrayList(); final PBounds bounds = new PBounds(50, 50, 10, 10); n.findIntersectingNodes(bounds, found); @@ -550,14 +551,14 @@ public void testGetClientPropertiesShouldReturnSameCollectionAlways() { } public void testGetClientPropertyKeysEnumerationShouldReturnEnumarationOnNewNode() { - final Enumeration enumeration = node.getClientPropertyKeysEnumeration(); + final Enumeration enumeration = node.getClientPropertyKeysEnumeration(); assertNotNull(enumeration); assertFalse(enumeration.hasMoreElements()); } public void testGetClientPropertyKeysEnumerationShouldReturnCorrectEnumWhenPropertiesExist() { node.addAttribute("Testing", "Hello"); - final Enumeration enumeration = node.getClientPropertyKeysEnumeration(); + final Enumeration enumeration = node.getClientPropertyKeysEnumeration(); assertNotNull(enumeration); assertTrue(enumeration.hasMoreElements()); assertEquals("Testing", enumeration.nextElement()); @@ -596,7 +597,7 @@ public void testGetIntegerAttributeReturnsDefaultWhenProvided() { } public void testGetIntegerAttributeReturnsValueIfFoundWhenDefaultProvided() { - node.addAttribute("Found", new Integer(5)); + node.addAttribute("Found", Integer.valueOf(5)); assertEquals(5, node.getIntegerAttribute("Found", 10)); } @@ -605,7 +606,7 @@ public void testGetDoubleAttributeReturnsDefaultWhenProvided() { } public void testGetDoubleAttributeReturnsValueIfFoundWhenDefaultProvided() { - node.addAttribute("Found", new Double(5)); + node.addAttribute("Found", Double.valueOf(5)); assertEquals(5, node.getIntegerAttribute("Found", 10), 0.001); } @@ -1199,7 +1200,7 @@ public void testfullPickReturnsFalseWhenNotOverlappingWithChildNode() { } public void testAddChildrenAddsAllChildren() { - final Collection newChildren = new ArrayList(); + final Collection newChildren = new ArrayList(); newChildren.add(new PNode()); newChildren.add(new PNode()); newChildren.add(new PNode()); @@ -1210,7 +1211,7 @@ public void testAddChildrenAddsAllChildren() { } public void testRemoveChildrenWorks() { - final Collection newChildren = new ArrayList(); + final Collection newChildren = new ArrayList(); newChildren.add(new PNode()); newChildren.add(new PNode()); newChildren.add(new PNode()); @@ -1222,7 +1223,7 @@ public void testRemoveChildrenWorks() { } public void testGetAllNodesUnrollsTheNodeGraph() { - final Collection newChildren = new ArrayList(); + final Collection newChildren = new ArrayList(); newChildren.add(new PNode()); newChildren.add(new PNode()); newChildren.add(new PNode()); @@ -1262,7 +1263,7 @@ public void testReplaceWithSwapsParents() { } public void testGetChildrenIteratorReturnsIteratorEvenWithNoChildren() { - final ListIterator iterator = node.getChildrenIterator(); + final ListIterator iterator = node.getChildrenIterator(); assertNotNull(iterator); assertFalse(iterator.hasNext()); } @@ -1271,7 +1272,7 @@ public void testGetChildrenIteratorReturnsValidIteratorWhenHasChildren() { final PNode child = new PNode(); node.addChild(child); - final ListIterator iterator = node.getChildrenIterator(); + final ListIterator iterator = node.getChildrenIterator(); assertNotNull(iterator); assertTrue(iterator.hasNext()); assertEquals(child, iterator.next()); @@ -1293,7 +1294,7 @@ public boolean acceptChildrenOf(final PNode aNode) { node.addChild(new PNode()); node.addChild(new PNode()); node.addChild(new PNode()); - final Collection nodes = node.getAllNodes(nullFilter, null); + final Collection nodes = node.getAllNodes(nullFilter, null); assertNotNull(nodes); assertTrue(nodes.isEmpty()); } diff --git a/core/src/test/java/org/piccolo2d/POffscreenCanvasTest.java b/core/src/test/java/org/piccolo2d/POffscreenCanvasTest.java index 49669684..31a84646 100644 --- a/core/src/test/java/org/piccolo2d/POffscreenCanvasTest.java +++ b/core/src/test/java/org/piccolo2d/POffscreenCanvasTest.java @@ -33,8 +33,6 @@ import java.awt.Graphics2D; import java.awt.image.BufferedImage; -import org.piccolo2d.PCamera; -import org.piccolo2d.POffscreenCanvas; import org.piccolo2d.nodes.PPath; import org.piccolo2d.util.PBounds; import org.piccolo2d.util.PPaintContext; diff --git a/core/src/test/java/org/piccolo2d/PRootTest.java b/core/src/test/java/org/piccolo2d/PRootTest.java index d65d5b62..6b87607c 100644 --- a/core/src/test/java/org/piccolo2d/PRootTest.java +++ b/core/src/test/java/org/piccolo2d/PRootTest.java @@ -33,7 +33,6 @@ import javax.swing.Timer; -import org.piccolo2d.PRoot; import org.piccolo2d.activities.PActivity; import junit.framework.TestCase; diff --git a/core/src/test/java/org/piccolo2d/PerformanceLog.java b/core/src/test/java/org/piccolo2d/PerformanceLog.java index ed20a6d7..04458ddc 100644 --- a/core/src/test/java/org/piccolo2d/PerformanceLog.java +++ b/core/src/test/java/org/piccolo2d/PerformanceLog.java @@ -36,7 +36,7 @@ */ public class PerformanceLog { - private final ArrayList log = new ArrayList(); + private final ArrayList log = new ArrayList(); private long testTime; @@ -76,7 +76,7 @@ public void writeLog() { System.out.println("Test data for input into spreadsheet:"); System.out.println(); - Iterator i = log.iterator(); + Iterator i = log.iterator(); while (i.hasNext()) { final ZLogEntry each = (ZLogEntry) i.next(); System.out.println(each.time); diff --git a/core/src/test/java/org/piccolo2d/PerformanceTests.java b/core/src/test/java/org/piccolo2d/PerformanceTests.java index 3c36cc00..cc6e47a4 100644 --- a/core/src/test/java/org/piccolo2d/PerformanceTests.java +++ b/core/src/test/java/org/piccolo2d/PerformanceTests.java @@ -40,7 +40,6 @@ import java.util.ArrayList; import java.util.Random; -import org.piccolo2d.PNode; import org.piccolo2d.nodes.PPath; import org.piccolo2d.util.PAffineTransform; import org.piccolo2d.util.PBounds; @@ -60,9 +59,9 @@ public PerformanceTests(final String name) { } public void testRunPerformanceTests() { - if (1 == 1) { - return; - } + //if (1 == 1) { + // return; + //} // three times to warm up JVM for (int i = 0; i < 3; i++) { @@ -126,7 +125,7 @@ public void addNodes() { public void removeNodes() { final PNode parent = new PNode(); final PNode[] nodes = new PNode[NUMBER_NODES]; - final ArrayList list = new ArrayList(); + final ArrayList list = new ArrayList(); for (int i = 0; i < NUMBER_NODES; i++) { nodes[i] = new PNode(); diff --git a/core/src/test/java/org/piccolo2d/PiccoloAsserts.java b/core/src/test/java/org/piccolo2d/PiccoloAsserts.java index 4cac1809..6bdc63f1 100644 --- a/core/src/test/java/org/piccolo2d/PiccoloAsserts.java +++ b/core/src/test/java/org/piccolo2d/PiccoloAsserts.java @@ -36,6 +36,7 @@ import junit.framework.Assert; + /** * This class provides helper methods to help with testing. * diff --git a/core/src/test/java/org/piccolo2d/SerializationTest.java b/core/src/test/java/org/piccolo2d/SerializationTest.java index 8f218613..8d409a81 100644 --- a/core/src/test/java/org/piccolo2d/SerializationTest.java +++ b/core/src/test/java/org/piccolo2d/SerializationTest.java @@ -30,8 +30,6 @@ import java.util.Iterator; -import org.piccolo2d.PLayer; -import org.piccolo2d.PNode; import org.piccolo2d.nodes.PPath; import org.piccolo2d.nodes.PText; @@ -59,7 +57,7 @@ public void test() { l = (PNode) l.clone(); // copy uses serialization internally assertTrue(l.getChildrenCount() == 300); - final Iterator i = l.getChildrenIterator(); + final Iterator i = l.getChildrenIterator(); while (i.hasNext()) { final PNode each = (PNode) i.next(); assertEquals(l, each.getParent()); diff --git a/core/src/test/java/org/piccolo2d/activities/PInterpolatingActivityTest.java b/core/src/test/java/org/piccolo2d/activities/PInterpolatingActivityTest.java index 9cd9fd5b..28e18038 100755 --- a/core/src/test/java/org/piccolo2d/activities/PInterpolatingActivityTest.java +++ b/core/src/test/java/org/piccolo2d/activities/PInterpolatingActivityTest.java @@ -28,7 +28,6 @@ */ package org.piccolo2d.activities; -import org.piccolo2d.activities.PInterpolatingActivity; import org.piccolo2d.util.PUtil; import junit.framework.TestCase; diff --git a/core/src/test/java/org/piccolo2d/activities/PTransformActivityTest.java b/core/src/test/java/org/piccolo2d/activities/PTransformActivityTest.java index 39141d99..7b8cdeb0 100644 --- a/core/src/test/java/org/piccolo2d/activities/PTransformActivityTest.java +++ b/core/src/test/java/org/piccolo2d/activities/PTransformActivityTest.java @@ -28,8 +28,6 @@ */ package org.piccolo2d.activities; -import org.piccolo2d.activities.PTransformActivity; - import junit.framework.TestCase; /** diff --git a/core/src/test/java/org/piccolo2d/event/MockPBasicInputEventHandler.java b/core/src/test/java/org/piccolo2d/event/MockPBasicInputEventHandler.java index 4bf67c88..f7fe572d 100644 --- a/core/src/test/java/org/piccolo2d/event/MockPBasicInputEventHandler.java +++ b/core/src/test/java/org/piccolo2d/event/MockPBasicInputEventHandler.java @@ -30,14 +30,11 @@ import java.util.ArrayList; -import org.piccolo2d.event.PBasicInputEventHandler; -import org.piccolo2d.event.PInputEvent; - /** * Mock PBasicInputEventHandler. */ public class MockPBasicInputEventHandler extends PBasicInputEventHandler { - private final ArrayList methodCalls = new ArrayList(); + private final ArrayList methodCalls = new ArrayList(); public String[] getMethodCalls() { final String[] result = new String[methodCalls.size()]; diff --git a/core/src/test/java/org/piccolo2d/event/PBasicInputEventListenerTest.java b/core/src/test/java/org/piccolo2d/event/PBasicInputEventListenerTest.java index 695c0781..b8cd0841 100644 --- a/core/src/test/java/org/piccolo2d/event/PBasicInputEventListenerTest.java +++ b/core/src/test/java/org/piccolo2d/event/PBasicInputEventListenerTest.java @@ -35,9 +35,6 @@ import org.piccolo2d.PCanvas; import org.piccolo2d.PiccoloAsserts; -import org.piccolo2d.event.PBasicInputEventHandler; -import org.piccolo2d.event.PInputEvent; -import org.piccolo2d.event.PInputEventFilter; import junit.framework.TestCase; diff --git a/core/src/test/java/org/piccolo2d/event/PDragEventHandlerTest.java b/core/src/test/java/org/piccolo2d/event/PDragEventHandlerTest.java index f6d52adf..53d34f37 100644 --- a/core/src/test/java/org/piccolo2d/event/PDragEventHandlerTest.java +++ b/core/src/test/java/org/piccolo2d/event/PDragEventHandlerTest.java @@ -28,8 +28,6 @@ */ package org.piccolo2d.event; -import org.piccolo2d.event.PDragEventHandler; - import junit.framework.TestCase; /** diff --git a/core/src/test/java/org/piccolo2d/event/PInputEventFilterTest.java b/core/src/test/java/org/piccolo2d/event/PInputEventFilterTest.java index 89fcf96f..4e3116db 100644 --- a/core/src/test/java/org/piccolo2d/event/PInputEventFilterTest.java +++ b/core/src/test/java/org/piccolo2d/event/PInputEventFilterTest.java @@ -34,8 +34,6 @@ import javax.swing.JPanel; import org.piccolo2d.PInputManager; -import org.piccolo2d.event.PInputEvent; -import org.piccolo2d.event.PInputEventFilter; import junit.framework.TestCase; @@ -175,34 +173,34 @@ public void testRejectsAcceptedEventIfAcceptsHandledEventsIsFalse() { public void testRejectsEventsUnlessModifiersContainAllOfMask() { PInputEvent event = buildTestEvent(); - filter.setAndMask(InputEvent.CTRL_MASK | InputEvent.ALT_MASK); + filter.setAndMask(InputEvent.CTRL_DOWN_MASK | InputEvent.ALT_DOWN_MASK); assertRejectsEvent(event); - event = buildTestEvent(InputEvent.CTRL_MASK | InputEvent.ALT_MASK); + event = buildTestEvent(InputEvent.CTRL_DOWN_MASK | InputEvent.ALT_DOWN_MASK); assertAcceptsEvent(event); - event = buildTestEvent(InputEvent.CTRL_MASK | InputEvent.ALT_MASK | InputEvent.META_MASK); + event = buildTestEvent(InputEvent.CTRL_DOWN_MASK | InputEvent.ALT_DOWN_MASK | InputEvent.META_DOWN_MASK); assertAcceptsEvent(event); } public void testRejectsEventsUnlessModifiersContainOneOfOrMask() { final PInputEvent event = buildTestEvent(); - filter.setOrMask(InputEvent.CTRL_MASK | InputEvent.ALT_MASK); + filter.setOrMask(InputEvent.CTRL_DOWN_MASK | InputEvent.ALT_DOWN_MASK); assertRejectsEvent(event); - assertRejectsEvent(buildTestEvent(InputEvent.META_MASK)); - assertAcceptsEvent(buildTestEvent(InputEvent.CTRL_MASK)); - assertAcceptsEvent(buildTestEvent(InputEvent.ALT_MASK)); - assertAcceptsEvent(buildTestEvent(InputEvent.CTRL_MASK | InputEvent.ALT_MASK)); + assertRejectsEvent(buildTestEvent(InputEvent.META_DOWN_MASK)); + assertAcceptsEvent(buildTestEvent(InputEvent.CTRL_DOWN_MASK)); + assertAcceptsEvent(buildTestEvent(InputEvent.ALT_DOWN_MASK)); + assertAcceptsEvent(buildTestEvent(InputEvent.CTRL_DOWN_MASK | InputEvent.ALT_DOWN_MASK)); } public void testRejectsEventsUnlessTheyMatchOneOfNotMask() { final PInputEvent event = buildTestEvent(); - filter.setNotMask(InputEvent.CTRL_MASK | InputEvent.ALT_MASK); + filter.setNotMask(InputEvent.CTRL_DOWN_MASK | InputEvent.ALT_DOWN_MASK); assertAcceptsEvent(event); - assertAcceptsEvent(buildTestEvent(InputEvent.META_MASK)); - assertRejectsEvent(buildTestEvent(InputEvent.CTRL_MASK)); - assertRejectsEvent(buildTestEvent(InputEvent.ALT_MASK)); - assertRejectsEvent(buildTestEvent(InputEvent.CTRL_MASK | InputEvent.ALT_MASK)); + assertAcceptsEvent(buildTestEvent(InputEvent.META_DOWN_MASK)); + assertRejectsEvent(buildTestEvent(InputEvent.CTRL_DOWN_MASK)); + assertRejectsEvent(buildTestEvent(InputEvent.ALT_DOWN_MASK)); + assertRejectsEvent(buildTestEvent(InputEvent.CTRL_DOWN_MASK | InputEvent.ALT_DOWN_MASK)); } public void testRejectsMouseEventsIfMouseClickFilterSet() { @@ -238,7 +236,7 @@ private void assertAcceptsEvent(final PInputEvent event) { } private PInputEvent buildTestEvent() { - return buildTestEvent(InputEvent.BUTTON1_MASK); + return buildTestEvent(InputEvent.BUTTON1_DOWN_MASK); } private PInputEvent buildTestEvent(final int modifiers) { diff --git a/core/src/test/java/org/piccolo2d/event/PInputEventTest.java b/core/src/test/java/org/piccolo2d/event/PInputEventTest.java index 6108fabe..2334368d 100644 --- a/core/src/test/java/org/piccolo2d/event/PInputEventTest.java +++ b/core/src/test/java/org/piccolo2d/event/PInputEventTest.java @@ -34,7 +34,6 @@ import org.piccolo2d.PCamera; import org.piccolo2d.PCanvas; -import org.piccolo2d.event.PInputEvent; import org.piccolo2d.util.PBounds; import org.piccolo2d.util.PPickPath; @@ -126,7 +125,7 @@ public void testHandledEventCanBeUnHandled() { } public void testReturnsCorrectModifiers() { - assertEquals(InputEvent.BUTTON1_MASK, mouseEvent.getModifiers()); + assertEquals(InputEvent.BUTTON1_DOWN_MASK, mouseEvent.getModifiersEx()); } public void testGetButtonUsesWhatWasPassedToMouseEvent() { @@ -134,7 +133,7 @@ public void testGetButtonUsesWhatWasPassedToMouseEvent() { } private MouseEvent buildSwingClick(final int x, final int y) { - return new MouseEvent(canvas, 1, System.currentTimeMillis(), InputEvent.BUTTON1_MASK, x, y, 1, false, + return new MouseEvent(canvas, 1, System.currentTimeMillis(), InputEvent.BUTTON1_DOWN_MASK, x, y, 1, false, MouseEvent.BUTTON1); } diff --git a/extras/src/test/java/org/piccolo2d/extras/event/PNotificationCenterTest.java b/core/src/test/java/org/piccolo2d/event/PNotificationCenterTest.java similarity index 96% rename from extras/src/test/java/org/piccolo2d/extras/event/PNotificationCenterTest.java rename to core/src/test/java/org/piccolo2d/event/PNotificationCenterTest.java index 17ec467b..e53c8a55 100644 --- a/extras/src/test/java/org/piccolo2d/extras/event/PNotificationCenterTest.java +++ b/core/src/test/java/org/piccolo2d/event/PNotificationCenterTest.java @@ -26,10 +26,10 @@ * 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.event; +package org.piccolo2d.event; -import org.piccolo2d.extras.event.PNotification; -import org.piccolo2d.extras.event.PNotificationCenter; +import org.piccolo2d.event.PNotification; +import org.piccolo2d.event.PNotificationCenter; import junit.framework.TestCase; diff --git a/core/src/test/java/org/piccolo2d/event/PPanEventHandlerTest.java b/core/src/test/java/org/piccolo2d/event/PPanEventHandlerTest.java index 0fee1c12..5a0c0e94 100644 --- a/core/src/test/java/org/piccolo2d/event/PPanEventHandlerTest.java +++ b/core/src/test/java/org/piccolo2d/event/PPanEventHandlerTest.java @@ -28,8 +28,6 @@ */ package org.piccolo2d.event; -import org.piccolo2d.event.PPanEventHandler; - import junit.framework.TestCase; /** diff --git a/extras/src/test/java/org/piccolo2d/extras/event/PSelectionEventHandlerTest.java b/core/src/test/java/org/piccolo2d/event/PSelectionEventHandlerTest.java similarity index 92% rename from extras/src/test/java/org/piccolo2d/extras/event/PSelectionEventHandlerTest.java rename to core/src/test/java/org/piccolo2d/event/PSelectionEventHandlerTest.java index 1309cf19..02cdbe4e 100755 --- a/extras/src/test/java/org/piccolo2d/extras/event/PSelectionEventHandlerTest.java +++ b/core/src/test/java/org/piccolo2d/event/PSelectionEventHandlerTest.java @@ -26,7 +26,7 @@ * 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.event; +package org.piccolo2d.event; import java.awt.event.KeyEvent; @@ -34,11 +34,9 @@ import org.piccolo2d.PLayer; import org.piccolo2d.PNode; import org.piccolo2d.event.PInputEvent; -import org.piccolo2d.extras.event.PNotification; -import org.piccolo2d.extras.event.PNotificationCenter; -import org.piccolo2d.extras.event.PSelectionEventHandler; - - +import org.piccolo2d.event.PNotification; +import org.piccolo2d.event.PNotificationCenter; +import org.piccolo2d.event.PSelectionEventHandler; import junit.framework.TestCase; @@ -94,7 +92,7 @@ public void testKeyboardDeleteFiresSelectionChange() PNotificationCenter notificationCenter = PNotificationCenter.defaultCenter(); notificationCenter.addListener(this, "selectionChanged", PSelectionEventHandler.SELECTION_CHANGED_NOTIFICATION, null); - KeyEvent keyEvent = new KeyEvent(canvas, -1, System.currentTimeMillis(), 0, KeyEvent.VK_DELETE); + KeyEvent keyEvent = new KeyEvent(canvas, -1, System.currentTimeMillis(), 0, KeyEvent.VK_DELETE, KeyEvent.CHAR_UNDEFINED); PInputEvent event = new PInputEvent(null, keyEvent); selectionHandler.keyPressed(event); assertTrue(selectionHandler.getSelectionReference().isEmpty()); @@ -116,7 +114,7 @@ public void testKeyboardDeleteInactive() PNotificationCenter notificationCenter = PNotificationCenter.defaultCenter(); notificationCenter.addListener(this, "selectionChanged", PSelectionEventHandler.SELECTION_CHANGED_NOTIFICATION, null); - KeyEvent keyEvent = new KeyEvent(canvas, -1, System.currentTimeMillis(), 0, KeyEvent.VK_DELETE); + KeyEvent keyEvent = new KeyEvent(canvas, -1, System.currentTimeMillis(), 0, KeyEvent.VK_DELETE, KeyEvent.CHAR_UNDEFINED); PInputEvent event = new PInputEvent(null, keyEvent); selectionHandler.keyPressed(event); assertTrue(selectionHandler.getSelectionReference().contains(node)); @@ -135,7 +133,7 @@ public void testKeyboardDeleteEmptySelection() PNotificationCenter notificationCenter = PNotificationCenter.defaultCenter(); notificationCenter.addListener(this, "selectionChanged", PSelectionEventHandler.SELECTION_CHANGED_NOTIFICATION, null); - KeyEvent keyEvent = new KeyEvent(canvas, -1, System.currentTimeMillis(), 0, KeyEvent.VK_DELETE); + KeyEvent keyEvent = new KeyEvent(canvas, -1, System.currentTimeMillis(), 0, KeyEvent.VK_DELETE, KeyEvent.CHAR_UNDEFINED); PInputEvent event = new PInputEvent(null, keyEvent); selectionHandler.keyPressed(event); assertTrue(selectionHandler.getSelectionReference().isEmpty()); diff --git a/core/src/test/java/org/piccolo2d/event/PZoomEventHandlerTest.java b/core/src/test/java/org/piccolo2d/event/PZoomEventHandlerTest.java index 74f5a4c2..ff3ff3af 100644 --- a/core/src/test/java/org/piccolo2d/event/PZoomEventHandlerTest.java +++ b/core/src/test/java/org/piccolo2d/event/PZoomEventHandlerTest.java @@ -28,8 +28,6 @@ */ package org.piccolo2d.event; -import org.piccolo2d.event.PZoomEventHandler; - import junit.framework.TestCase; /** diff --git a/extras/src/test/java/org/piccolo2d/extras/handles/PHandleTest.java b/core/src/test/java/org/piccolo2d/handles/PHandleTest.java similarity index 88% rename from extras/src/test/java/org/piccolo2d/extras/handles/PHandleTest.java rename to core/src/test/java/org/piccolo2d/handles/PHandleTest.java index 1b4e4c6d..d6565dc8 100644 --- a/extras/src/test/java/org/piccolo2d/extras/handles/PHandleTest.java +++ b/core/src/test/java/org/piccolo2d/handles/PHandleTest.java @@ -26,11 +26,11 @@ * 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.handles; +package org.piccolo2d.handles; import org.piccolo2d.PNode; -import org.piccolo2d.extras.handles.PHandle; -import org.piccolo2d.extras.util.PLocator; +import org.piccolo2d.handles.PHandle; +import org.piccolo2d.util.PLocator; import junit.framework.TestCase; @@ -69,7 +69,10 @@ public void testChangingLocatorWorks() { public void testChangingParentCausesRelocateHandle() { final int[] relocateCounts = new int[1]; PHandle handle = new PHandle(new OriginLocator()) { - public void relocateHandle() { + + private static final long serialVersionUID = 1L; + + public void relocateHandle() { super.relocateHandle(); relocateCounts[0]++; } @@ -83,7 +86,10 @@ public void relocateHandle() { public void testResizingParentCausesRelocateHandle() { final int[] relocateCounts = new int[1]; PHandle handle = new PHandle(new OriginLocator()) { - public void relocateHandle() { + + private static final long serialVersionUID = 1L; + + public void relocateHandle() { super.relocateHandle(); relocateCounts[0]++; } @@ -101,7 +107,10 @@ public void testLocatorCanBeNullWithoutAProblem() { } private final class OriginLocator extends PLocator { - public double locateX() { + + private static final long serialVersionUID = 1L; + + public double locateX() { return 0; } diff --git a/core/src/test/java/org/piccolo2d/nodes/AbstractPPathTest.java b/core/src/test/java/org/piccolo2d/nodes/AbstractPPathTest.java index 0276a125..0cc27298 100644 --- a/core/src/test/java/org/piccolo2d/nodes/AbstractPPathTest.java +++ b/core/src/test/java/org/piccolo2d/nodes/AbstractPPathTest.java @@ -28,15 +28,12 @@ */ package org.piccolo2d.nodes; -import java.awt.Color; import java.awt.Shape; - import java.awt.geom.AffineTransform; import java.awt.geom.Arc2D; import java.awt.geom.PathIterator; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; - import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; @@ -100,7 +97,7 @@ private PPath deserializeFromFile(final PBounds b, final File file) throws FileN final FileInputStream fin = new FileInputStream(file); final ObjectInputStream in = new ObjectInputStream(fin); path = (PPath) in.readObject(); - + in.close(); return path; } diff --git a/core/src/test/java/org/piccolo2d/nodes/MockPropertyChangeListener.java b/core/src/test/java/org/piccolo2d/nodes/MockPropertyChangeListener.java index 70323104..af3b00cb 100644 --- a/core/src/test/java/org/piccolo2d/nodes/MockPropertyChangeListener.java +++ b/core/src/test/java/org/piccolo2d/nodes/MockPropertyChangeListener.java @@ -38,7 +38,7 @@ * Mock PropertyChangeListener. */ public class MockPropertyChangeListener implements PropertyChangeListener { - private final List changes = new ArrayList(); + private final List changes = new ArrayList(); public void propertyChange(final PropertyChangeEvent evt) { changes.add(evt); diff --git a/extras/src/test/java/org/piccolo2d/extras/nodes/P3DRectTest.java b/core/src/test/java/org/piccolo2d/nodes/P3DRectTest.java similarity index 94% rename from extras/src/test/java/org/piccolo2d/extras/nodes/P3DRectTest.java rename to core/src/test/java/org/piccolo2d/nodes/P3DRectTest.java index b3b0ff2a..2cfe68a9 100644 --- a/extras/src/test/java/org/piccolo2d/extras/nodes/P3DRectTest.java +++ b/core/src/test/java/org/piccolo2d/nodes/P3DRectTest.java @@ -26,13 +26,13 @@ * 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.nodes; +package org.piccolo2d.nodes; import java.awt.Color; import java.awt.Graphics2D; import java.awt.image.BufferedImage; -import org.piccolo2d.extras.nodes.P3DRect; +import org.piccolo2d.nodes.P3DRect; import org.piccolo2d.util.PPaintContext; import junit.framework.TestCase; diff --git a/extras/src/test/java/org/piccolo2d/extras/nodes/PCacheCameraTest.java b/core/src/test/java/org/piccolo2d/nodes/PCacheCameraTest.java similarity index 93% rename from extras/src/test/java/org/piccolo2d/extras/nodes/PCacheCameraTest.java rename to core/src/test/java/org/piccolo2d/nodes/PCacheCameraTest.java index 52d3a79a..b4e36f15 100644 --- a/extras/src/test/java/org/piccolo2d/extras/nodes/PCacheCameraTest.java +++ b/core/src/test/java/org/piccolo2d/nodes/PCacheCameraTest.java @@ -26,9 +26,9 @@ * 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.nodes; +package org.piccolo2d.nodes; -import org.piccolo2d.extras.nodes.PCacheCamera; +import org.piccolo2d.nodes.PCacheCamera; import junit.framework.TestCase; diff --git a/extras/src/test/java/org/piccolo2d/extras/nodes/PClipTest.java b/core/src/test/java/org/piccolo2d/nodes/PClipTest.java similarity index 94% rename from extras/src/test/java/org/piccolo2d/extras/nodes/PClipTest.java rename to core/src/test/java/org/piccolo2d/nodes/PClipTest.java index 98788783..5c400e24 100644 --- a/extras/src/test/java/org/piccolo2d/extras/nodes/PClipTest.java +++ b/core/src/test/java/org/piccolo2d/nodes/PClipTest.java @@ -26,9 +26,9 @@ * 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.nodes; +package org.piccolo2d.nodes; -import org.piccolo2d.extras.nodes.PClip; +import org.piccolo2d.nodes.PClip; import junit.framework.TestCase; diff --git a/extras/src/test/java/org/piccolo2d/extras/nodes/PCompositeTest.java b/core/src/test/java/org/piccolo2d/nodes/PCompositeTest.java similarity index 94% rename from extras/src/test/java/org/piccolo2d/extras/nodes/PCompositeTest.java rename to core/src/test/java/org/piccolo2d/nodes/PCompositeTest.java index 8a802d9b..b8120799 100644 --- a/extras/src/test/java/org/piccolo2d/extras/nodes/PCompositeTest.java +++ b/core/src/test/java/org/piccolo2d/nodes/PCompositeTest.java @@ -26,9 +26,9 @@ * 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.nodes; +package org.piccolo2d.nodes; -import org.piccolo2d.extras.nodes.PComposite; +import org.piccolo2d.nodes.PComposite; import junit.framework.TestCase; diff --git a/core/src/test/java/org/piccolo2d/nodes/PHtmlViewTest.java b/core/src/test/java/org/piccolo2d/nodes/PHtmlViewTest.java index 66a4ee73..466e0b5a 100644 --- a/core/src/test/java/org/piccolo2d/nodes/PHtmlViewTest.java +++ b/core/src/test/java/org/piccolo2d/nodes/PHtmlViewTest.java @@ -35,7 +35,6 @@ import org.piccolo2d.MockPropertyChangeListener; import org.piccolo2d.PCanvas; -import org.piccolo2d.nodes.PHtmlView; import org.piccolo2d.util.PBounds; import junit.framework.TestCase; diff --git a/core/src/test/java/org/piccolo2d/nodes/PImageTest.java b/core/src/test/java/org/piccolo2d/nodes/PImageTest.java index 6d7d54d1..e3e719ba 100644 --- a/core/src/test/java/org/piccolo2d/nodes/PImageTest.java +++ b/core/src/test/java/org/piccolo2d/nodes/PImageTest.java @@ -34,7 +34,6 @@ import javax.imageio.ImageIO; -import org.piccolo2d.nodes.PImage; import org.piccolo2d.util.PPaintContext; import junit.framework.TestCase; diff --git a/extras/src/test/java/org/piccolo2d/extras/nodes/PLensTest.java b/core/src/test/java/org/piccolo2d/nodes/PLensTest.java similarity index 94% rename from extras/src/test/java/org/piccolo2d/extras/nodes/PLensTest.java rename to core/src/test/java/org/piccolo2d/nodes/PLensTest.java index 72d897f8..0f7bfa3a 100644 --- a/extras/src/test/java/org/piccolo2d/extras/nodes/PLensTest.java +++ b/core/src/test/java/org/piccolo2d/nodes/PLensTest.java @@ -26,9 +26,9 @@ * 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.nodes; +package org.piccolo2d.nodes; -import org.piccolo2d.extras.nodes.PLens; +import org.piccolo2d.nodes.PLens; import junit.framework.TestCase; diff --git a/extras/src/test/java/org/piccolo2d/extras/nodes/PLineTest.java b/core/src/test/java/org/piccolo2d/nodes/PLineTest.java similarity index 94% rename from extras/src/test/java/org/piccolo2d/extras/nodes/PLineTest.java rename to core/src/test/java/org/piccolo2d/nodes/PLineTest.java index 6f953759..3372e023 100644 --- a/extras/src/test/java/org/piccolo2d/extras/nodes/PLineTest.java +++ b/core/src/test/java/org/piccolo2d/nodes/PLineTest.java @@ -26,11 +26,11 @@ * 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.nodes; +package org.piccolo2d.nodes; import java.awt.Color; -import org.piccolo2d.extras.nodes.PLine; +import org.piccolo2d.nodes.PLine; import junit.framework.TestCase; diff --git a/extras/src/test/java/org/piccolo2d/extras/nodes/PNodeCacheTest.java b/core/src/test/java/org/piccolo2d/nodes/PNodeCacheTest.java similarity index 94% rename from extras/src/test/java/org/piccolo2d/extras/nodes/PNodeCacheTest.java rename to core/src/test/java/org/piccolo2d/nodes/PNodeCacheTest.java index b1067def..103c99e0 100644 --- a/extras/src/test/java/org/piccolo2d/extras/nodes/PNodeCacheTest.java +++ b/core/src/test/java/org/piccolo2d/nodes/PNodeCacheTest.java @@ -26,9 +26,9 @@ * 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.nodes; +package org.piccolo2d.nodes; -import org.piccolo2d.extras.nodes.PNodeCache; +import org.piccolo2d.nodes.PNodeCache; import junit.framework.TestCase; diff --git a/extras/src/test/java/org/piccolo2d/extras/nodes/PShadowTest.java b/core/src/test/java/org/piccolo2d/nodes/PShadowTest.java similarity index 95% rename from extras/src/test/java/org/piccolo2d/extras/nodes/PShadowTest.java rename to core/src/test/java/org/piccolo2d/nodes/PShadowTest.java index 5bd58520..ac6ef6b2 100755 --- a/extras/src/test/java/org/piccolo2d/extras/nodes/PShadowTest.java +++ b/core/src/test/java/org/piccolo2d/nodes/PShadowTest.java @@ -26,15 +26,14 @@ * 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.nodes; +package org.piccolo2d.nodes; import java.awt.Color; import java.awt.Graphics2D; import java.awt.Paint; - import java.awt.image.BufferedImage; -import org.piccolo2d.extras.nodes.PShadow; +import org.piccolo2d.nodes.PShadow; import junit.framework.TestCase; diff --git a/extras/src/test/java/org/piccolo2d/extras/nodes/PStyledTextTest.java b/core/src/test/java/org/piccolo2d/nodes/PStyledTextTest.java similarity index 93% rename from extras/src/test/java/org/piccolo2d/extras/nodes/PStyledTextTest.java rename to core/src/test/java/org/piccolo2d/nodes/PStyledTextTest.java index 99fd00fe..28c31437 100644 --- a/extras/src/test/java/org/piccolo2d/extras/nodes/PStyledTextTest.java +++ b/core/src/test/java/org/piccolo2d/nodes/PStyledTextTest.java @@ -26,9 +26,9 @@ * 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.nodes; +package org.piccolo2d.nodes; -import org.piccolo2d.extras.nodes.PStyledText; +import org.piccolo2d.nodes.PStyledText; import junit.framework.TestCase; diff --git a/core/src/test/java/org/piccolo2d/nodes/PTextTest.java b/core/src/test/java/org/piccolo2d/nodes/PTextTest.java index 380ae1cd..a1b7de93 100644 --- a/core/src/test/java/org/piccolo2d/nodes/PTextTest.java +++ b/core/src/test/java/org/piccolo2d/nodes/PTextTest.java @@ -33,7 +33,6 @@ import java.awt.Font; import org.piccolo2d.MockPropertyChangeListener; -import org.piccolo2d.nodes.PText; import junit.framework.TestCase; diff --git a/extras/src/test/java/org/piccolo2d/extras/pswing/PComboBoxTest.java b/core/src/test/java/org/piccolo2d/pswing/PComboBoxTest.java similarity index 89% rename from extras/src/test/java/org/piccolo2d/extras/pswing/PComboBoxTest.java rename to core/src/test/java/org/piccolo2d/pswing/PComboBoxTest.java index 2edc6f02..9400bd3f 100644 --- a/extras/src/test/java/org/piccolo2d/extras/pswing/PComboBoxTest.java +++ b/core/src/test/java/org/piccolo2d/pswing/PComboBoxTest.java @@ -26,15 +26,15 @@ * 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.pswing; +package org.piccolo2d.pswing; import java.util.Vector; import javax.swing.DefaultComboBoxModel; -import org.piccolo2d.extras.pswing.PComboBox; -import org.piccolo2d.extras.pswing.PSwing; -import org.piccolo2d.extras.pswing.PSwingCanvas; +import org.piccolo2d.pswing.PComboBox; +import org.piccolo2d.pswing.PSwing; +import org.piccolo2d.pswing.PSwingCanvas; import junit.framework.TestCase; @@ -48,7 +48,7 @@ public void testPComboInstallsItsOwnUI() { } public void testConstructsWithVector() { - final Vector items = new Vector(); + final Vector items = new Vector(); items.add("A"); items.add("B"); final PComboBox combo = new PComboBox(items); @@ -62,7 +62,7 @@ public void testConstructsWithArray() { } public void testConstructsWithComboBoxModel() { - final DefaultComboBoxModel model = new DefaultComboBoxModel(); + final DefaultComboBoxModel model = new DefaultComboBoxModel(); model.addElement("A"); model.addElement("B"); final PComboBox combo = new PComboBox(model); diff --git a/extras/src/test/java/org/piccolo2d/extras/pswing/PSwingCanvasTest.java b/core/src/test/java/org/piccolo2d/pswing/PSwingCanvasTest.java similarity index 92% rename from extras/src/test/java/org/piccolo2d/extras/pswing/PSwingCanvasTest.java rename to core/src/test/java/org/piccolo2d/pswing/PSwingCanvasTest.java index 3040dc99..509b0180 100644 --- a/extras/src/test/java/org/piccolo2d/extras/pswing/PSwingCanvasTest.java +++ b/core/src/test/java/org/piccolo2d/pswing/PSwingCanvasTest.java @@ -26,12 +26,12 @@ * 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.pswing; +package org.piccolo2d.pswing; import javax.swing.JLabel; -import org.piccolo2d.extras.pswing.PSwing; -import org.piccolo2d.extras.pswing.PSwingCanvas; +import org.piccolo2d.pswing.PSwing; +import org.piccolo2d.pswing.PSwingCanvas; import junit.framework.TestCase; diff --git a/extras/src/test/java/org/piccolo2d/extras/pswing/PSwingDynamicComponentExample.java b/core/src/test/java/org/piccolo2d/pswing/PSwingDynamicComponentExample.java similarity index 96% rename from extras/src/test/java/org/piccolo2d/extras/pswing/PSwingDynamicComponentExample.java rename to core/src/test/java/org/piccolo2d/pswing/PSwingDynamicComponentExample.java index 6ef741c0..c64a3893 100644 --- a/extras/src/test/java/org/piccolo2d/extras/pswing/PSwingDynamicComponentExample.java +++ b/core/src/test/java/org/piccolo2d/pswing/PSwingDynamicComponentExample.java @@ -26,12 +26,16 @@ * 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.pswing; +package org.piccolo2d.pswing; import javax.swing.*; import javax.swing.border.CompoundBorder; import javax.swing.border.EmptyBorder; import javax.swing.border.LineBorder; + +import org.piccolo2d.pswing.PSwing; +import org.piccolo2d.pswing.PSwingCanvas; + import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; @@ -56,7 +60,9 @@ */ public class PSwingDynamicComponentExample extends JFrame { - private static final Dimension FRAME_SIZE = new Dimension( 800, 400 ); + private static final long serialVersionUID = 1L; + + private static final Dimension FRAME_SIZE = new Dimension( 800, 400 ); private static final int TEXT_FIELD_COLUMNS = 30; private final ComponentPanel swingPanel, piccoloPanel; @@ -175,7 +181,9 @@ private void updatePanels() { // A panel with a few different types of JComponent. private static class ComponentPanel extends JPanel { - // allow public access to keep our example code short + private static final long serialVersionUID = 1L; + + // allow public access to keep our example code short public final JLabel label; public final JCheckBox checkBox; public final JRadioButton radioButton; diff --git a/core/src/test/java/org/piccolo2d/pswing/PSwingEventHandlerTest.java b/core/src/test/java/org/piccolo2d/pswing/PSwingEventHandlerTest.java new file mode 100644 index 00000000..fb0c6c69 --- /dev/null +++ b/core/src/test/java/org/piccolo2d/pswing/PSwingEventHandlerTest.java @@ -0,0 +1,11 @@ +package org.piccolo2d.pswing; + +import junit.framework.TestCase; + +public class PSwingEventHandlerTest extends TestCase { + + public void testConstructorAcceptsNullTargetNode() { + //PSwingCanvas canvas = new PSwingCanvas(); + //PSwingEventHandler handler = new PSwingEventHandler(canvas, null); + } +} diff --git a/extras/src/test/java/org/piccolo2d/extras/pswing/PSwingMouseEventTest.java b/core/src/test/java/org/piccolo2d/pswing/PSwingMouseEventTest.java similarity index 90% rename from extras/src/test/java/org/piccolo2d/extras/pswing/PSwingMouseEventTest.java rename to core/src/test/java/org/piccolo2d/pswing/PSwingMouseEventTest.java index cb82afa1..30355c32 100644 --- a/extras/src/test/java/org/piccolo2d/extras/pswing/PSwingMouseEventTest.java +++ b/core/src/test/java/org/piccolo2d/pswing/PSwingMouseEventTest.java @@ -1,4 +1,4 @@ -package org.piccolo2d.extras.pswing; +package org.piccolo2d.pswing; import java.awt.event.MouseEvent; import java.awt.event.MouseWheelEvent; @@ -7,10 +7,10 @@ import javax.swing.JPanel; import org.piccolo2d.event.PInputEvent; -import org.piccolo2d.extras.pswing.PSwingEvent; -import org.piccolo2d.extras.pswing.PSwingMouseEvent; -import org.piccolo2d.extras.pswing.PSwingMouseMotionEvent; -import org.piccolo2d.extras.pswing.PSwingMouseWheelEvent; +import org.piccolo2d.pswing.PSwingEvent; +import org.piccolo2d.pswing.PSwingMouseEvent; +import org.piccolo2d.pswing.PSwingMouseMotionEvent; +import org.piccolo2d.pswing.PSwingMouseWheelEvent; import junit.framework.TestCase; diff --git a/extras/src/test/java/org/piccolo2d/extras/pswing/PSwingRepaintManagerTest.java b/core/src/test/java/org/piccolo2d/pswing/PSwingRepaintManagerTest.java similarity index 95% rename from extras/src/test/java/org/piccolo2d/extras/pswing/PSwingRepaintManagerTest.java rename to core/src/test/java/org/piccolo2d/pswing/PSwingRepaintManagerTest.java index 4c526b2b..690168c2 100644 --- a/extras/src/test/java/org/piccolo2d/extras/pswing/PSwingRepaintManagerTest.java +++ b/core/src/test/java/org/piccolo2d/pswing/PSwingRepaintManagerTest.java @@ -26,7 +26,7 @@ * 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.pswing; +package org.piccolo2d.pswing; import java.awt.Canvas; import java.awt.Component; @@ -35,8 +35,8 @@ import javax.swing.JPanel; import javax.swing.RepaintManager; -import org.piccolo2d.extras.pswing.PSwingCanvas; -import org.piccolo2d.extras.pswing.PSwingRepaintManager; +import org.piccolo2d.pswing.PSwingCanvas; +import org.piccolo2d.pswing.PSwingRepaintManager; import junit.framework.TestCase; diff --git a/extras/src/test/java/org/piccolo2d/extras/pswing/PSwingTest.java b/core/src/test/java/org/piccolo2d/pswing/PSwingTest.java similarity index 94% rename from extras/src/test/java/org/piccolo2d/extras/pswing/PSwingTest.java rename to core/src/test/java/org/piccolo2d/pswing/PSwingTest.java index 47d62faa..c50e6f1a 100644 --- a/extras/src/test/java/org/piccolo2d/extras/pswing/PSwingTest.java +++ b/core/src/test/java/org/piccolo2d/pswing/PSwingTest.java @@ -26,9 +26,13 @@ * 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.pswing; +package org.piccolo2d.pswing; import junit.framework.TestCase; + +import org.piccolo2d.pswing.PSwing; +import org.piccolo2d.pswing.PSwingCanvas; +import org.piccolo2d.pswing.PSwingRepaintManager; import org.piccolo2d.util.PPaintContext; import javax.swing.JButton; @@ -74,7 +78,10 @@ public void testPSwingResizesItselfWhenComponentIsResized() { final JPanel panel = new JPanel(); new PSwing(panel) { - public void updateBounds() { + + private static final long serialVersionUID = 1L; + + public void updateBounds() { super.updateBounds(); reshaped[0] = true; @@ -120,7 +127,7 @@ public void testHidingPNodeHidesComponent() { public void testAddingSwingComponentToWrappedHierarchyMakesItNotDoubleBuffer() { final JPanel panel = new JPanel(); - final PSwing pSwing = new PSwing(panel); + //final PSwing pSwing = new PSwing(panel); final JComponent child = new JLabel("Test Component"); child.setDoubleBuffered(true); panel.add(child); @@ -294,7 +301,10 @@ public void testPSwingRegistersWithCanvasThroughoutItsLifeCycle() { } public class MockPaintingPSwing extends PSwing { - private boolean paintedGreek; + + private static final long serialVersionUID = 1L; + + private boolean paintedGreek; private boolean paintedComponent; public MockPaintingPSwing(JComponent component) { diff --git a/extras/src/test/java/org/piccolo2d/extras/pswing/package.html b/core/src/test/java/org/piccolo2d/pswing/package.html similarity index 100% rename from extras/src/test/java/org/piccolo2d/extras/pswing/package.html rename to core/src/test/java/org/piccolo2d/pswing/package.html diff --git a/core/src/test/java/org/piccolo2d/util/PAffineTransformTest.java b/core/src/test/java/org/piccolo2d/util/PAffineTransformTest.java index c4e28ff3..61f0fbd2 100644 --- a/core/src/test/java/org/piccolo2d/util/PAffineTransformTest.java +++ b/core/src/test/java/org/piccolo2d/util/PAffineTransformTest.java @@ -34,10 +34,6 @@ import java.awt.geom.Rectangle2D; import org.piccolo2d.PiccoloAsserts; -import org.piccolo2d.util.PAffineTransform; -import org.piccolo2d.util.PAffineTransformException; -import org.piccolo2d.util.PBounds; -import org.piccolo2d.util.PDimension; import junit.framework.TestCase; diff --git a/core/src/test/java/org/piccolo2d/util/PBoundsTest.java b/core/src/test/java/org/piccolo2d/util/PBoundsTest.java index fcac2702..d77d1c20 100644 --- a/core/src/test/java/org/piccolo2d/util/PBoundsTest.java +++ b/core/src/test/java/org/piccolo2d/util/PBoundsTest.java @@ -31,8 +31,6 @@ import java.awt.geom.Rectangle2D; import org.piccolo2d.PiccoloAsserts; -import org.piccolo2d.util.PBounds; -import org.piccolo2d.util.PDimension; import junit.framework.TestCase; diff --git a/core/src/test/java/org/piccolo2d/util/PDebugTest.java b/core/src/test/java/org/piccolo2d/util/PDebugTest.java index e1fccad0..1ea5059d 100644 --- a/core/src/test/java/org/piccolo2d/util/PDebugTest.java +++ b/core/src/test/java/org/piccolo2d/util/PDebugTest.java @@ -30,8 +30,6 @@ import java.awt.Color; -import org.piccolo2d.util.PDebug; - import junit.framework.TestCase; /** diff --git a/core/src/test/java/org/piccolo2d/util/PDimensionTest.java b/core/src/test/java/org/piccolo2d/util/PDimensionTest.java index 2040b609..86b748c6 100644 --- a/core/src/test/java/org/piccolo2d/util/PDimensionTest.java +++ b/core/src/test/java/org/piccolo2d/util/PDimensionTest.java @@ -32,8 +32,6 @@ import java.awt.geom.Dimension2D; import java.awt.geom.Point2D; -import org.piccolo2d.util.PDimension; - import junit.framework.TestCase; /** diff --git a/core/src/test/java/org/piccolo2d/util/PObjectOutputStreamTest.java b/core/src/test/java/org/piccolo2d/util/PObjectOutputStreamTest.java index 1656c96c..07cf90b9 100644 --- a/core/src/test/java/org/piccolo2d/util/PObjectOutputStreamTest.java +++ b/core/src/test/java/org/piccolo2d/util/PObjectOutputStreamTest.java @@ -31,8 +31,6 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; -import org.piccolo2d.util.PObjectOutputStream; - import junit.framework.TestCase; /** diff --git a/core/src/test/java/org/piccolo2d/util/PPickPathTest.java b/core/src/test/java/org/piccolo2d/util/PPickPathTest.java index 026e65b9..f80a0c81 100644 --- a/core/src/test/java/org/piccolo2d/util/PPickPathTest.java +++ b/core/src/test/java/org/piccolo2d/util/PPickPathTest.java @@ -33,7 +33,6 @@ import org.piccolo2d.PLayer; import org.piccolo2d.PNode; import org.piccolo2d.nodes.PPath; -import org.piccolo2d.util.PPickPath; import junit.framework.TestCase; diff --git a/extras/src/test/java/org/piccolo2d/extras/util/ShadowUtilsTest.java b/core/src/test/java/org/piccolo2d/util/ShadowUtilsTest.java similarity index 94% rename from extras/src/test/java/org/piccolo2d/extras/util/ShadowUtilsTest.java rename to core/src/test/java/org/piccolo2d/util/ShadowUtilsTest.java index 93d7ac90..f42b2ca3 100755 --- a/extras/src/test/java/org/piccolo2d/extras/util/ShadowUtilsTest.java +++ b/core/src/test/java/org/piccolo2d/util/ShadowUtilsTest.java @@ -26,16 +26,14 @@ * 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.util; +package org.piccolo2d.util; import java.awt.Color; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.image.BufferedImage; -import org.piccolo2d.extras.nodes.PShadow; -import org.piccolo2d.extras.util.ShadowUtils; - +import org.piccolo2d.util.ShadowUtils; import junit.framework.TestCase; diff --git a/examples/src/main/java/org/piccolo2d/examples/ActivityExample.java b/examples/src/main/java/org/piccolo2d/examples/ActivityExample.java index 676842a1..6cd85be9 100644 --- a/examples/src/main/java/org/piccolo2d/examples/ActivityExample.java +++ b/examples/src/main/java/org/piccolo2d/examples/ActivityExample.java @@ -31,10 +31,10 @@ import java.awt.Color; import org.piccolo2d.PCanvas; +import org.piccolo2d.PFrame; import org.piccolo2d.PLayer; import org.piccolo2d.PNode; import org.piccolo2d.activities.PActivity; -import org.piccolo2d.extras.PFrame; import org.piccolo2d.nodes.PPath; diff --git a/examples/src/main/java/org/piccolo2d/examples/AngleNodeExample.java b/examples/src/main/java/org/piccolo2d/examples/AngleNodeExample.java index f1ea095e..e7d245ed 100644 --- a/examples/src/main/java/org/piccolo2d/examples/AngleNodeExample.java +++ b/examples/src/main/java/org/piccolo2d/examples/AngleNodeExample.java @@ -37,13 +37,13 @@ import java.awt.geom.Rectangle2D; import org.piccolo2d.PCanvas; +import org.piccolo2d.PFrame; import org.piccolo2d.PLayer; import org.piccolo2d.PNode; import org.piccolo2d.event.PInputEvent; -import org.piccolo2d.extras.PFrame; -import org.piccolo2d.extras.handles.PHandle; -import org.piccolo2d.extras.util.PLocator; +import org.piccolo2d.handles.PHandle; import org.piccolo2d.util.PDimension; +import org.piccolo2d.util.PLocator; import org.piccolo2d.util.PPaintContext; diff --git a/examples/src/main/java/org/piccolo2d/examples/AnimatePathExample.java b/examples/src/main/java/org/piccolo2d/examples/AnimatePathExample.java index 9986d93e..187407de 100755 --- a/examples/src/main/java/org/piccolo2d/examples/AnimatePathExample.java +++ b/examples/src/main/java/org/piccolo2d/examples/AnimatePathExample.java @@ -34,15 +34,12 @@ import java.awt.Stroke; import org.piccolo2d.PCanvas; - +import org.piccolo2d.PFrame; import org.piccolo2d.activities.PInterpolatingActivity; import org.piccolo2d.event.PBasicInputEventHandler; import org.piccolo2d.event.PInputEvent; import org.piccolo2d.event.PInputEventListener; - -import org.piccolo2d.extras.PFrame; - import org.piccolo2d.nodes.PPath; /** diff --git a/examples/src/main/java/org/piccolo2d/examples/BirdsEyeViewExample.java b/examples/src/main/java/org/piccolo2d/examples/BirdsEyeViewExample.java index 166865e9..7d8a0a7e 100644 --- a/examples/src/main/java/org/piccolo2d/examples/BirdsEyeViewExample.java +++ b/examples/src/main/java/org/piccolo2d/examples/BirdsEyeViewExample.java @@ -40,14 +40,14 @@ import javax.swing.JDialog; import org.piccolo2d.PCanvas; +import org.piccolo2d.PFrame; import org.piccolo2d.PLayer; import org.piccolo2d.PNode; import org.piccolo2d.event.PBasicInputEventHandler; import org.piccolo2d.event.PDragEventHandler; import org.piccolo2d.event.PDragSequenceEventHandler; import org.piccolo2d.event.PInputEvent; -import org.piccolo2d.extras.PFrame; -import org.piccolo2d.extras.nodes.P3DRect; +import org.piccolo2d.nodes.P3DRect; import org.piccolo2d.nodes.PImage; import org.piccolo2d.nodes.PPath; import org.piccolo2d.nodes.PText; diff --git a/examples/src/main/java/org/piccolo2d/examples/CameraExample.java b/examples/src/main/java/org/piccolo2d/examples/CameraExample.java index 22ebabbc..6e33ebdf 100644 --- a/examples/src/main/java/org/piccolo2d/examples/CameraExample.java +++ b/examples/src/main/java/org/piccolo2d/examples/CameraExample.java @@ -32,9 +32,9 @@ import org.piccolo2d.PCamera; import org.piccolo2d.PCanvas; +import org.piccolo2d.PFrame; import org.piccolo2d.PLayer; -import org.piccolo2d.extras.PFrame; -import org.piccolo2d.extras.handles.PBoundsHandle; +import org.piccolo2d.handles.PBoundsHandle; import org.piccolo2d.nodes.PPath; diff --git a/examples/src/main/java/org/piccolo2d/examples/CenterExample.java b/examples/src/main/java/org/piccolo2d/examples/CenterExample.java index aec8ece8..e3ac4fb2 100644 --- a/examples/src/main/java/org/piccolo2d/examples/CenterExample.java +++ b/examples/src/main/java/org/piccolo2d/examples/CenterExample.java @@ -30,8 +30,8 @@ import org.piccolo2d.PCamera; import org.piccolo2d.PCanvas; +import org.piccolo2d.PFrame; import org.piccolo2d.PLayer; -import org.piccolo2d.extras.PFrame; import org.piccolo2d.nodes.PPath; diff --git a/examples/src/main/java/org/piccolo2d/examples/ChartLabelExample.java b/examples/src/main/java/org/piccolo2d/examples/ChartLabelExample.java index 4233dd9e..f86ba462 100644 --- a/examples/src/main/java/org/piccolo2d/examples/ChartLabelExample.java +++ b/examples/src/main/java/org/piccolo2d/examples/ChartLabelExample.java @@ -32,10 +32,10 @@ import java.awt.geom.Point2D; import org.piccolo2d.PCanvas; +import org.piccolo2d.PFrame; import org.piccolo2d.PLayer; import org.piccolo2d.event.PDragSequenceEventHandler; import org.piccolo2d.event.PInputEvent; -import org.piccolo2d.extras.PFrame; import org.piccolo2d.nodes.PPath; import org.piccolo2d.nodes.PText; diff --git a/examples/src/main/java/org/piccolo2d/examples/ClipExample.java b/examples/src/main/java/org/piccolo2d/examples/ClipExample.java index 2a509d05..07437d15 100644 --- a/examples/src/main/java/org/piccolo2d/examples/ClipExample.java +++ b/examples/src/main/java/org/piccolo2d/examples/ClipExample.java @@ -33,9 +33,9 @@ import java.awt.geom.Ellipse2D; import org.piccolo2d.PCanvas; +import org.piccolo2d.PFrame; import org.piccolo2d.event.PDragEventHandler; -import org.piccolo2d.extras.PFrame; -import org.piccolo2d.extras.nodes.PClip; +import org.piccolo2d.nodes.PClip; import org.piccolo2d.nodes.PPath; diff --git a/examples/src/main/java/org/piccolo2d/examples/CompositeExample.java b/examples/src/main/java/org/piccolo2d/examples/CompositeExample.java index 6a1b0e04..9a1e8e67 100644 --- a/examples/src/main/java/org/piccolo2d/examples/CompositeExample.java +++ b/examples/src/main/java/org/piccolo2d/examples/CompositeExample.java @@ -31,10 +31,10 @@ import java.awt.Color; import org.piccolo2d.PCanvas; +import org.piccolo2d.PFrame; import org.piccolo2d.PNode; import org.piccolo2d.event.PDragEventHandler; -import org.piccolo2d.extras.PFrame; -import org.piccolo2d.extras.nodes.PComposite; +import org.piccolo2d.nodes.PComposite; import org.piccolo2d.nodes.PPath; import org.piccolo2d.nodes.PText; diff --git a/examples/src/main/java/org/piccolo2d/examples/DynamicExample.java b/examples/src/main/java/org/piccolo2d/examples/DynamicExample.java index d284f3f6..80f4405a 100644 --- a/examples/src/main/java/org/piccolo2d/examples/DynamicExample.java +++ b/examples/src/main/java/org/piccolo2d/examples/DynamicExample.java @@ -34,13 +34,13 @@ import java.util.Random; import org.piccolo2d.PCanvas; +import org.piccolo2d.PFrame; import org.piccolo2d.PLayer; import org.piccolo2d.PNode; import org.piccolo2d.PRoot; import org.piccolo2d.activities.PActivity; -import org.piccolo2d.extras.PFrame; -import org.piccolo2d.extras.util.PFixedWidthStroke; import org.piccolo2d.nodes.PPath; +import org.piccolo2d.util.PFixedWidthStroke; /** @@ -94,7 +94,7 @@ public void activityStep(final long currentTime) { } public void rotateNodes() { - final Iterator i = getCanvas().getLayer().getChildrenReference().iterator(); + final Iterator i = getCanvas().getLayer().getChildrenReference().iterator(); while (i.hasNext()) { final PNode each = (PNode) i.next(); each.rotate(Math.toRadians(2)); diff --git a/examples/src/main/java/org/piccolo2d/examples/EventHandlerExample.java b/examples/src/main/java/org/piccolo2d/examples/EventHandlerExample.java index 7073a319..1fa56d46 100644 --- a/examples/src/main/java/org/piccolo2d/examples/EventHandlerExample.java +++ b/examples/src/main/java/org/piccolo2d/examples/EventHandlerExample.java @@ -33,11 +33,11 @@ import java.awt.geom.Point2D; import org.piccolo2d.PCanvas; +import org.piccolo2d.PFrame; import org.piccolo2d.PLayer; import org.piccolo2d.event.PBasicInputEventHandler; import org.piccolo2d.event.PInputEvent; import org.piccolo2d.event.PInputEventFilter; -import org.piccolo2d.extras.PFrame; import org.piccolo2d.nodes.PPath; import org.piccolo2d.util.PBounds; @@ -71,7 +71,7 @@ public void initialize() { // Make the event handler only work with BUTTON1 events, so that it does // not conflict with the zoom event handler that is installed by // default. - rectEventHandler.setEventFilter(new PInputEventFilter(InputEvent.BUTTON1_MASK)); + rectEventHandler.setEventFilter(new PInputEventFilter(InputEvent.BUTTON1_DOWN_MASK)); // Remove the pan event handler that is installed by default so that it // does not conflict with our new rectangle creation event handler. diff --git a/examples/src/main/java/org/piccolo2d/examples/ExampleRunner.java b/examples/src/main/java/org/piccolo2d/examples/ExampleRunner.java index 845760c2..37cba364 100644 --- a/examples/src/main/java/org/piccolo2d/examples/ExampleRunner.java +++ b/examples/src/main/java/org/piccolo2d/examples/ExampleRunner.java @@ -45,7 +45,7 @@ import javax.swing.WindowConstants; import javax.swing.border.TitledBorder; -import org.piccolo2d.extras.PFrame; +import org.piccolo2d.PFrame; import org.piccolo2d.util.PDebug; @@ -71,7 +71,7 @@ public void createExampleButtons() { JPanel panel= new JPanel(new GridLayout(0, 2)); c.add(BorderLayout.CENTER, panel); - addExampleButtons(panel, new Class[] { ActivityExample.class, AngleNodeExample.class, + addExampleButtons(panel, new Class[] { ActivityExample.class, AngleNodeExample.class, BirdsEyeViewExample.class, CameraExample.class, CenterExample.class, ChartLabelExample.class, ClipExample.class, CompositeExample.class, DynamicExample.class, EventHandlerExample.class, FullScreenNodeExample.class, GraphEditorExample.class, GridExample.class, GroupExample.class, @@ -93,18 +93,27 @@ private JPanel buildOptions() { optionsPanel.setBorder(new TitledBorder("Display Options")); optionsPanel.add(new JCheckBox(new AbstractAction("Print Frame Rates to Console") { - public void actionPerformed(final ActionEvent e) { + + private static final long serialVersionUID = 1L; + + public void actionPerformed(final ActionEvent e) { PDebug.debugPrintFrameRate = !PDebug.debugPrintFrameRate; } })); optionsPanel.add(new JCheckBox(new AbstractAction("Show Region Managment") { - public void actionPerformed(final ActionEvent e) { + + private static final long serialVersionUID = 1L; + + public void actionPerformed(final ActionEvent e) { PDebug.debugRegionManagement = !PDebug.debugRegionManagement; } })); optionsPanel.add(new JCheckBox(new AbstractAction("Show Full Bounds") { + + private static final long serialVersionUID = 1L; + public void actionPerformed(final ActionEvent e) { PDebug.debugFullBounds = !PDebug.debugFullBounds; } @@ -113,20 +122,23 @@ public void actionPerformed(final ActionEvent e) { return optionsPanel; } - private void addExampleButtons(final JPanel panel, final Class[] exampleClasses) { + private void addExampleButtons(final JPanel panel, final Class[] exampleClasses) { for (int i = 0; i < exampleClasses.length; i++) { panel.add(buildExampleButton(exampleClasses[i])); } } - private JButton buildExampleButton(final Class exampleClass) { + private JButton buildExampleButton(final Class exampleClass) { final String fullClassName = exampleClass.getName(); final String simpleClassName = fullClassName.substring(fullClassName.lastIndexOf('.') + 1); final String exampleLabel = camelToProper(simpleClassName); JButton button = new JButton(new AbstractAction(exampleLabel) { - public void actionPerformed(final ActionEvent event) { + + private static final long serialVersionUID = 1L; + + public void actionPerformed(final ActionEvent event) { try { - final PFrame example = (PFrame) exampleClass.newInstance(); + final PFrame example = (PFrame)exampleClass.getDeclaredConstructor().newInstance(); example.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); } catch (final Exception e) { diff --git a/examples/src/main/java/org/piccolo2d/examples/FullBoundsEventExample.java b/examples/src/main/java/org/piccolo2d/examples/FullBoundsEventExample.java index 0917b81a..36f7a121 100755 --- a/examples/src/main/java/org/piccolo2d/examples/FullBoundsEventExample.java +++ b/examples/src/main/java/org/piccolo2d/examples/FullBoundsEventExample.java @@ -37,7 +37,7 @@ import javax.swing.Timer; import org.piccolo2d.PCanvas; -import org.piccolo2d.extras.PFrame; +import org.piccolo2d.PFrame; import org.piccolo2d.nodes.PText; /** diff --git a/examples/src/main/java/org/piccolo2d/examples/GraphEditorExample.java b/examples/src/main/java/org/piccolo2d/examples/GraphEditorExample.java index d507f20e..62c0880a 100644 --- a/examples/src/main/java/org/piccolo2d/examples/GraphEditorExample.java +++ b/examples/src/main/java/org/piccolo2d/examples/GraphEditorExample.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008-2019, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 2008-2011, Piccolo2D project, http://piccolo2d.org * Copyright (c) 1998-2008, University of Maryland * All rights reserved. * @@ -34,11 +34,11 @@ import java.util.Random; import org.piccolo2d.PCanvas; +import org.piccolo2d.PFrame; import org.piccolo2d.PLayer; import org.piccolo2d.PNode; import org.piccolo2d.event.PDragSequenceEventHandler; import org.piccolo2d.event.PInputEvent; -import org.piccolo2d.extras.PFrame; import org.piccolo2d.nodes.PPath; @@ -73,12 +73,12 @@ public void initialize() { final PLayer edgeLayer = new PLayer(); getCanvas().getCamera().addLayer(0, edgeLayer); final Random rnd = new Random(); - ArrayList tmp; + ArrayList tmp; for (int i = 0; i < numNodes; i++) { final float x = (float) (300. * rnd.nextDouble()); final float y = (float) (400. * rnd.nextDouble()); final PPath path = PPath.createEllipse(x, y, 20, 20); - tmp = new ArrayList(); + tmp = new ArrayList(); path.addAttribute("edges", tmp); nodeLayer.addChild(path); } @@ -103,7 +103,7 @@ public void initialize() { tmp = (ArrayList) node2.getAttribute("edges"); tmp.add(edge); - tmp = new ArrayList(); + tmp = new ArrayList(); tmp.add(node1); tmp.add(node2); edge.addAttribute("nodes", tmp); @@ -153,12 +153,12 @@ public void drag(final PInputEvent e) { final PNode node = e.getPickedNode(); node.translate(e.getDelta().width, e.getDelta().height); - final ArrayList edges = (ArrayList) e.getPickedNode().getAttribute("edges"); + final ArrayList edges = (ArrayList) e.getPickedNode().getAttribute("edges"); int i; for (i = 0; i < edges.size(); i++) { final PPath edge = (PPath) edges.get(i); - final ArrayList nodes = (ArrayList) edge.getAttribute("nodes"); + final ArrayList nodes = (ArrayList) edge.getAttribute("nodes"); final PNode node1 = (PNode) nodes.get(0); final PNode node2 = (PNode) nodes.get(1); diff --git a/examples/src/main/java/org/piccolo2d/examples/GridExample.java b/examples/src/main/java/org/piccolo2d/examples/GridExample.java index 27734263..bf13348b 100644 --- a/examples/src/main/java/org/piccolo2d/examples/GridExample.java +++ b/examples/src/main/java/org/piccolo2d/examples/GridExample.java @@ -40,12 +40,12 @@ import org.piccolo2d.PCamera; import org.piccolo2d.PCanvas; +import org.piccolo2d.PFrame; import org.piccolo2d.PLayer; import org.piccolo2d.PNode; import org.piccolo2d.PRoot; import org.piccolo2d.event.PDragSequenceEventHandler; import org.piccolo2d.event.PInputEvent; -import org.piccolo2d.extras.PFrame; import org.piccolo2d.util.PPaintContext; diff --git a/examples/src/main/java/org/piccolo2d/examples/GroupExample.java b/examples/src/main/java/org/piccolo2d/examples/GroupExample.java index 9e051ec6..94a3a62a 100644 --- a/examples/src/main/java/org/piccolo2d/examples/GroupExample.java +++ b/examples/src/main/java/org/piccolo2d/examples/GroupExample.java @@ -35,9 +35,9 @@ import org.piccolo2d.PCamera; import org.piccolo2d.PCanvas; +import org.piccolo2d.PFrame; import org.piccolo2d.PNode; -import org.piccolo2d.extras.PFrame; -import org.piccolo2d.extras.event.PSelectionEventHandler; +import org.piccolo2d.event.PSelectionEventHandler; import org.piccolo2d.nodes.PPath; import org.piccolo2d.util.PBounds; import org.piccolo2d.util.PPaintContext; @@ -109,7 +109,7 @@ public void initialize() { // Create a selection handler so we can see that the decorator actually // works - final ArrayList selectableParents = new ArrayList(); + final ArrayList selectableParents = new ArrayList(); selectableParents.add(dg); selectableParents.add(vdg); diff --git a/examples/src/main/java/org/piccolo2d/examples/HandleExample.java b/examples/src/main/java/org/piccolo2d/examples/HandleExample.java index 9d2df523..39c99fb8 100644 --- a/examples/src/main/java/org/piccolo2d/examples/HandleExample.java +++ b/examples/src/main/java/org/piccolo2d/examples/HandleExample.java @@ -32,14 +32,14 @@ import java.awt.Color; import org.piccolo2d.PCanvas; +import org.piccolo2d.PFrame; import org.piccolo2d.event.PBasicInputEventHandler; import org.piccolo2d.event.PInputEvent; -import org.piccolo2d.extras.PFrame; -import org.piccolo2d.extras.handles.PBoundsHandle; -import org.piccolo2d.extras.handles.PHandle; -import org.piccolo2d.extras.util.PNodeLocator; +import org.piccolo2d.handles.PBoundsHandle; +import org.piccolo2d.handles.PHandle; import org.piccolo2d.nodes.PPath; import org.piccolo2d.util.PDimension; +import org.piccolo2d.util.PNodeLocator; /** diff --git a/examples/src/main/java/org/piccolo2d/examples/HelloWorldExample.java b/examples/src/main/java/org/piccolo2d/examples/HelloWorldExample.java index 9faa505a..5f47642f 100644 --- a/examples/src/main/java/org/piccolo2d/examples/HelloWorldExample.java +++ b/examples/src/main/java/org/piccolo2d/examples/HelloWorldExample.java @@ -29,7 +29,7 @@ package org.piccolo2d.examples; import org.piccolo2d.PCanvas; -import org.piccolo2d.extras.PFrame; +import org.piccolo2d.PFrame; import org.piccolo2d.nodes.PText; diff --git a/examples/src/main/java/org/piccolo2d/examples/HierarchyZoomExample.java b/examples/src/main/java/org/piccolo2d/examples/HierarchyZoomExample.java index df294c2b..36275028 100644 --- a/examples/src/main/java/org/piccolo2d/examples/HierarchyZoomExample.java +++ b/examples/src/main/java/org/piccolo2d/examples/HierarchyZoomExample.java @@ -29,10 +29,10 @@ package org.piccolo2d.examples; import org.piccolo2d.PCanvas; +import org.piccolo2d.PFrame; import org.piccolo2d.PNode; import org.piccolo2d.event.PBasicInputEventHandler; import org.piccolo2d.event.PInputEvent; -import org.piccolo2d.extras.PFrame; import org.piccolo2d.nodes.PPath; diff --git a/examples/src/main/java/org/piccolo2d/examples/HtmlViewExample.java b/examples/src/main/java/org/piccolo2d/examples/HtmlViewExample.java index 8eaf6902..9d07a4f3 100644 --- a/examples/src/main/java/org/piccolo2d/examples/HtmlViewExample.java +++ b/examples/src/main/java/org/piccolo2d/examples/HtmlViewExample.java @@ -33,10 +33,10 @@ import javax.swing.JOptionPane; import org.piccolo2d.PCanvas; +import org.piccolo2d.PFrame; import org.piccolo2d.PNode; import org.piccolo2d.event.PBasicInputEventHandler; import org.piccolo2d.event.PInputEvent; -import org.piccolo2d.extras.PFrame; import org.piccolo2d.nodes.PHtmlView; diff --git a/examples/src/main/java/org/piccolo2d/examples/KeyEventFocusExample.java b/examples/src/main/java/org/piccolo2d/examples/KeyEventFocusExample.java index 349fb81c..b20b4d15 100644 --- a/examples/src/main/java/org/piccolo2d/examples/KeyEventFocusExample.java +++ b/examples/src/main/java/org/piccolo2d/examples/KeyEventFocusExample.java @@ -31,10 +31,10 @@ import java.awt.Color; import org.piccolo2d.PCanvas; +import org.piccolo2d.PFrame; import org.piccolo2d.PNode; import org.piccolo2d.event.PBasicInputEventHandler; import org.piccolo2d.event.PInputEvent; -import org.piccolo2d.extras.PFrame; import org.piccolo2d.nodes.PPath; diff --git a/examples/src/main/java/org/piccolo2d/examples/LayoutExample.java b/examples/src/main/java/org/piccolo2d/examples/LayoutExample.java index 0c83c150..0acbf977 100644 --- a/examples/src/main/java/org/piccolo2d/examples/LayoutExample.java +++ b/examples/src/main/java/org/piccolo2d/examples/LayoutExample.java @@ -32,9 +32,9 @@ import java.util.Iterator; import org.piccolo2d.PCanvas; +import org.piccolo2d.PFrame; import org.piccolo2d.PNode; -import org.piccolo2d.extras.PFrame; -import org.piccolo2d.extras.handles.PBoundsHandle; +import org.piccolo2d.handles.PBoundsHandle; import org.piccolo2d.nodes.PPath; @@ -44,9 +44,6 @@ */ public class LayoutExample extends PFrame { - /** - * - */ private static final long serialVersionUID = 1L; public LayoutExample() { @@ -71,9 +68,9 @@ public void layoutChildren() { double xOffset = 0; final double yOffset = 0; - final Iterator i = getChildrenIterator(); + final Iterator i = getChildrenIterator(); while (i.hasNext()) { - final PNode each = (PNode) i.next(); + final PNode each = i.next(); each.setOffset(xOffset - each.getX(), yOffset); xOffset += each.getWidth(); } diff --git a/examples/src/main/java/org/piccolo2d/examples/LensExample.java b/examples/src/main/java/org/piccolo2d/examples/LensExample.java index d2e56d9a..a51a6515 100644 --- a/examples/src/main/java/org/piccolo2d/examples/LensExample.java +++ b/examples/src/main/java/org/piccolo2d/examples/LensExample.java @@ -34,14 +34,14 @@ import org.piccolo2d.PCamera; import org.piccolo2d.PCanvas; +import org.piccolo2d.PFrame; import org.piccolo2d.PLayer; import org.piccolo2d.PNode; import org.piccolo2d.PRoot; import org.piccolo2d.event.PDragSequenceEventHandler; import org.piccolo2d.event.PInputEvent; -import org.piccolo2d.extras.PFrame; -import org.piccolo2d.extras.handles.PBoundsHandle; -import org.piccolo2d.extras.nodes.PLens; +import org.piccolo2d.handles.PBoundsHandle; +import org.piccolo2d.nodes.PLens; import org.piccolo2d.nodes.PPath; import org.piccolo2d.nodes.PText; import org.piccolo2d.util.PPaintContext; diff --git a/examples/src/main/java/org/piccolo2d/examples/MouseWheelZoomExample.java b/examples/src/main/java/org/piccolo2d/examples/MouseWheelZoomExample.java index cc03976f..874d04ca 100644 --- a/examples/src/main/java/org/piccolo2d/examples/MouseWheelZoomExample.java +++ b/examples/src/main/java/org/piccolo2d/examples/MouseWheelZoomExample.java @@ -41,8 +41,8 @@ import javax.swing.JToolBar; import org.piccolo2d.PCanvas; +import org.piccolo2d.PFrame; import org.piccolo2d.event.PMouseWheelZoomEventHandler; -import org.piccolo2d.extras.PFrame; import org.piccolo2d.nodes.PPath; import org.piccolo2d.nodes.PText; diff --git a/examples/src/main/java/org/piccolo2d/examples/NavigationExample.java b/examples/src/main/java/org/piccolo2d/examples/NavigationExample.java index e47ad890..3f51d0fa 100644 --- a/examples/src/main/java/org/piccolo2d/examples/NavigationExample.java +++ b/examples/src/main/java/org/piccolo2d/examples/NavigationExample.java @@ -33,9 +33,9 @@ import java.util.Random; import org.piccolo2d.PCanvas; +import org.piccolo2d.PFrame; import org.piccolo2d.PLayer; -import org.piccolo2d.extras.PFrame; -import org.piccolo2d.extras.event.PNavigationEventHandler; +import org.piccolo2d.event.PNavigationEventHandler; import org.piccolo2d.nodes.PPath; diff --git a/examples/src/main/java/org/piccolo2d/examples/NodeCacheExample.java b/examples/src/main/java/org/piccolo2d/examples/NodeCacheExample.java index 03e54d6a..5d111a69 100644 --- a/examples/src/main/java/org/piccolo2d/examples/NodeCacheExample.java +++ b/examples/src/main/java/org/piccolo2d/examples/NodeCacheExample.java @@ -32,9 +32,9 @@ import java.awt.Color; import org.piccolo2d.PCanvas; +import org.piccolo2d.PFrame; import org.piccolo2d.event.PDragEventHandler; -import org.piccolo2d.extras.PFrame; -import org.piccolo2d.extras.nodes.PNodeCache; +import org.piccolo2d.nodes.PNodeCache; import org.piccolo2d.nodes.PPath; diff --git a/examples/src/main/java/org/piccolo2d/examples/NodeEventExample.java b/examples/src/main/java/org/piccolo2d/examples/NodeEventExample.java index 17dd4984..062e3f4d 100644 --- a/examples/src/main/java/org/piccolo2d/examples/NodeEventExample.java +++ b/examples/src/main/java/org/piccolo2d/examples/NodeEventExample.java @@ -32,11 +32,11 @@ import java.awt.geom.Dimension2D; import org.piccolo2d.PCanvas; +import org.piccolo2d.PFrame; import org.piccolo2d.PLayer; import org.piccolo2d.PNode; import org.piccolo2d.event.PBasicInputEventHandler; import org.piccolo2d.event.PInputEvent; -import org.piccolo2d.extras.PFrame; import org.piccolo2d.nodes.PPath; diff --git a/examples/src/main/java/org/piccolo2d/examples/NodeExample.java b/examples/src/main/java/org/piccolo2d/examples/NodeExample.java index c46f85ee..48f0d9a7 100644 --- a/examples/src/main/java/org/piccolo2d/examples/NodeExample.java +++ b/examples/src/main/java/org/piccolo2d/examples/NodeExample.java @@ -35,12 +35,12 @@ import java.awt.geom.Line2D; import org.piccolo2d.PCanvas; +import org.piccolo2d.PFrame; import org.piccolo2d.PLayer; import org.piccolo2d.PNode; import org.piccolo2d.event.PBasicInputEventHandler; import org.piccolo2d.event.PDragEventHandler; import org.piccolo2d.event.PInputEvent; -import org.piccolo2d.extras.PFrame; import org.piccolo2d.nodes.PImage; import org.piccolo2d.nodes.PPath; import org.piccolo2d.nodes.PText; diff --git a/examples/src/main/java/org/piccolo2d/examples/NodeLinkExample.java b/examples/src/main/java/org/piccolo2d/examples/NodeLinkExample.java index a57abc7a..2fc2aea6 100644 --- a/examples/src/main/java/org/piccolo2d/examples/NodeLinkExample.java +++ b/examples/src/main/java/org/piccolo2d/examples/NodeLinkExample.java @@ -34,9 +34,9 @@ import java.beans.PropertyChangeListener; import org.piccolo2d.PCanvas; +import org.piccolo2d.PFrame; import org.piccolo2d.PNode; import org.piccolo2d.event.PDragEventHandler; -import org.piccolo2d.extras.PFrame; import org.piccolo2d.nodes.PPath; diff --git a/examples/src/main/java/org/piccolo2d/examples/OffsetVsTranslateExample.java b/examples/src/main/java/org/piccolo2d/examples/OffsetVsTranslateExample.java index 990762b9..79cfdc8f 100644 --- a/examples/src/main/java/org/piccolo2d/examples/OffsetVsTranslateExample.java +++ b/examples/src/main/java/org/piccolo2d/examples/OffsetVsTranslateExample.java @@ -29,9 +29,9 @@ package org.piccolo2d.examples; import org.piccolo2d.PCanvas; +import org.piccolo2d.PFrame; import org.piccolo2d.PNode; import org.piccolo2d.activities.PActivity; -import org.piccolo2d.extras.PFrame; import org.piccolo2d.nodes.PText; diff --git a/examples/src/main/java/org/piccolo2d/examples/P3DRectExample.java b/examples/src/main/java/org/piccolo2d/examples/P3DRectExample.java index a67134ce..bec97046 100644 --- a/examples/src/main/java/org/piccolo2d/examples/P3DRectExample.java +++ b/examples/src/main/java/org/piccolo2d/examples/P3DRectExample.java @@ -29,15 +29,17 @@ package org.piccolo2d.examples; import org.piccolo2d.PCanvas; -import org.piccolo2d.extras.PFrame; -import org.piccolo2d.extras.nodes.P3DRect; +import org.piccolo2d.PFrame; +import org.piccolo2d.nodes.P3DRect; import java.awt.Color; public class P3DRectExample extends PFrame { - public P3DRectExample() { + private static final long serialVersionUID = 1L; + + public P3DRectExample() { this(null); } diff --git a/examples/src/main/java/org/piccolo2d/examples/PSwingExample.java b/examples/src/main/java/org/piccolo2d/examples/PSwingExample.java index 3fc4024b..602469f9 100644 --- a/examples/src/main/java/org/piccolo2d/examples/PSwingExample.java +++ b/examples/src/main/java/org/piccolo2d/examples/PSwingExample.java @@ -34,10 +34,10 @@ import javax.swing.event.ChangeListener; import org.piccolo2d.PCanvas; +import org.piccolo2d.PFrame; import org.piccolo2d.PLayer; -import org.piccolo2d.extras.PFrame; -import org.piccolo2d.extras.pswing.PSwing; -import org.piccolo2d.extras.pswing.PSwingCanvas; +import org.piccolo2d.pswing.PSwing; +import org.piccolo2d.pswing.PSwingCanvas; /** diff --git a/examples/src/main/java/org/piccolo2d/examples/PSwingScaleExample.java b/examples/src/main/java/org/piccolo2d/examples/PSwingScaleExample.java index 5d4b5f29..a0796ae4 100644 --- a/examples/src/main/java/org/piccolo2d/examples/PSwingScaleExample.java +++ b/examples/src/main/java/org/piccolo2d/examples/PSwingScaleExample.java @@ -1,11 +1,11 @@ package org.piccolo2d.examples; import org.piccolo2d.PCamera; +import org.piccolo2d.PFrame; import org.piccolo2d.PLayer; import org.piccolo2d.PNode; -import org.piccolo2d.extras.PFrame; -import org.piccolo2d.extras.pswing.PSwing; -import org.piccolo2d.extras.pswing.PSwingCanvas; +import org.piccolo2d.pswing.PSwing; +import org.piccolo2d.pswing.PSwingCanvas; import org.piccolo2d.util.PBounds; import javax.swing.JButton; @@ -17,11 +17,13 @@ /** * Demonstrate that PSwing nodes properly receive events even when they are parented by nodes * with extreme scales. This is an effective regression test that previously failed before fix - * applied to {@link org.piccolo2d.extras.pswing.PSwingEventHandler}. + * applied to {@link org.piccolo2d.pswing.PSwingEventHandler}. */ public class PSwingScaleExample extends PFrame { - public static void main(String[] args) { + private static final long serialVersionUID = 1L; + + public static void main(String[] args) { new PSwingScaleExample(); } diff --git a/examples/src/main/java/org/piccolo2d/examples/PanToExample.java b/examples/src/main/java/org/piccolo2d/examples/PanToExample.java index bbf5f70d..99315983 100644 --- a/examples/src/main/java/org/piccolo2d/examples/PanToExample.java +++ b/examples/src/main/java/org/piccolo2d/examples/PanToExample.java @@ -34,10 +34,10 @@ import org.piccolo2d.PCamera; import org.piccolo2d.PCanvas; +import org.piccolo2d.PFrame; import org.piccolo2d.PLayer; import org.piccolo2d.event.PBasicInputEventHandler; import org.piccolo2d.event.PInputEvent; -import org.piccolo2d.extras.PFrame; import org.piccolo2d.nodes.PPath; diff --git a/examples/src/main/java/org/piccolo2d/examples/PathExample.java b/examples/src/main/java/org/piccolo2d/examples/PathExample.java index 7533e842..e60f5781 100644 --- a/examples/src/main/java/org/piccolo2d/examples/PathExample.java +++ b/examples/src/main/java/org/piccolo2d/examples/PathExample.java @@ -36,13 +36,10 @@ import java.awt.geom.Arc2D; import org.piccolo2d.PCanvas; - +import org.piccolo2d.PFrame; import org.piccolo2d.event.PBasicInputEventHandler; import org.piccolo2d.event.PInputEvent; import org.piccolo2d.event.PInputEventListener; - -import org.piccolo2d.extras.PFrame; - import org.piccolo2d.nodes.PPath; /** diff --git a/examples/src/main/java/org/piccolo2d/examples/PositionExample.java b/examples/src/main/java/org/piccolo2d/examples/PositionExample.java index a5b67c83..4559e13a 100644 --- a/examples/src/main/java/org/piccolo2d/examples/PositionExample.java +++ b/examples/src/main/java/org/piccolo2d/examples/PositionExample.java @@ -31,8 +31,8 @@ import java.awt.Color; import org.piccolo2d.PCanvas; +import org.piccolo2d.PFrame; import org.piccolo2d.PNode; -import org.piccolo2d.extras.PFrame; import org.piccolo2d.nodes.PPath; diff --git a/examples/src/main/java/org/piccolo2d/examples/PositionPathActivityExample.java b/examples/src/main/java/org/piccolo2d/examples/PositionPathActivityExample.java index ca384328..3faba66d 100644 --- a/examples/src/main/java/org/piccolo2d/examples/PositionPathActivityExample.java +++ b/examples/src/main/java/org/piccolo2d/examples/PositionPathActivityExample.java @@ -31,10 +31,10 @@ import java.awt.geom.Arc2D; import java.awt.geom.GeneralPath; +import org.piccolo2d.PFrame; import org.piccolo2d.PLayer; import org.piccolo2d.PNode; -import org.piccolo2d.extras.PFrame; -import org.piccolo2d.extras.activities.PPositionPathActivity; +import org.piccolo2d.activities.PPositionPathActivity; import org.piccolo2d.nodes.PPath; diff --git a/examples/src/main/java/org/piccolo2d/examples/PrintExample.java b/examples/src/main/java/org/piccolo2d/examples/PrintExample.java index 7c6dfb2e..6745fbe4 100644 --- a/examples/src/main/java/org/piccolo2d/examples/PrintExample.java +++ b/examples/src/main/java/org/piccolo2d/examples/PrintExample.java @@ -52,13 +52,13 @@ import javax.swing.JToolBar; import org.piccolo2d.PCanvas; +import org.piccolo2d.PFrame; import org.piccolo2d.PLayer; -import org.piccolo2d.extras.PFrame; -import org.piccolo2d.extras.swing.PDefaultScrollDirector; -import org.piccolo2d.extras.swing.PScrollDirector; -import org.piccolo2d.extras.swing.PScrollPane; -import org.piccolo2d.extras.swing.PViewport; import org.piccolo2d.nodes.PPath; +import org.piccolo2d.swing.PDefaultScrollDirector; +import org.piccolo2d.swing.PScrollDirector; +import org.piccolo2d.swing.PScrollPane; +import org.piccolo2d.swing.PViewport; import org.piccolo2d.util.PAffineTransform; import org.piccolo2d.util.PBounds; @@ -185,9 +185,9 @@ public Point getViewPosition(final Rectangle2D viewBounds) { if (camera != null) { // First we compute the union of all the layers final PBounds layerBounds = new PBounds(); - final java.util.List layers = camera.getLayersReference(); - for (final Iterator i = layers.iterator(); i.hasNext();) { - final PLayer layer = (PLayer) i.next(); + final java.util.List layers = camera.getLayersReference(); + for (final Iterator i = layers.iterator(); i.hasNext();) { + final PLayer layer = i.next(); layerBounds.add(layer.getFullBoundsReference()); } @@ -233,9 +233,9 @@ public void setViewPosition(final double x, final double y) { // Get the union of all the layers' bounds final PBounds layerBounds = new PBounds(); - final List layers = camera.getLayersReference(); - for (final Iterator i = layers.iterator(); i.hasNext();) { - final PLayer layer = (PLayer) i.next(); + final List layers = camera.getLayersReference(); + for (final Iterator i = layers.iterator(); i.hasNext();) { + final PLayer layer = i.next(); layerBounds.add(layer.getFullBoundsReference()); } diff --git a/examples/src/main/java/org/piccolo2d/examples/PulseExample.java b/examples/src/main/java/org/piccolo2d/examples/PulseExample.java index 03a514b7..c7b4a03c 100644 --- a/examples/src/main/java/org/piccolo2d/examples/PulseExample.java +++ b/examples/src/main/java/org/piccolo2d/examples/PulseExample.java @@ -31,13 +31,13 @@ import java.awt.Color; import org.piccolo2d.PCanvas; +import org.piccolo2d.PFrame; import org.piccolo2d.PLayer; import org.piccolo2d.PNode; import org.piccolo2d.PRoot; import org.piccolo2d.activities.PActivityScheduler; import org.piccolo2d.activities.PColorActivity; import org.piccolo2d.activities.PInterpolatingActivity; -import org.piccolo2d.extras.PFrame; import org.piccolo2d.nodes.PPath; diff --git a/examples/src/main/java/org/piccolo2d/examples/ScrollingExample.java b/examples/src/main/java/org/piccolo2d/examples/ScrollingExample.java index 6e04ee8f..0b59f954 100644 --- a/examples/src/main/java/org/piccolo2d/examples/ScrollingExample.java +++ b/examples/src/main/java/org/piccolo2d/examples/ScrollingExample.java @@ -43,13 +43,13 @@ import javax.swing.JToolBar; import org.piccolo2d.PCanvas; +import org.piccolo2d.PFrame; import org.piccolo2d.PLayer; -import org.piccolo2d.extras.PFrame; -import org.piccolo2d.extras.swing.PDefaultScrollDirector; -import org.piccolo2d.extras.swing.PScrollDirector; -import org.piccolo2d.extras.swing.PScrollPane; -import org.piccolo2d.extras.swing.PViewport; import org.piccolo2d.nodes.PPath; +import org.piccolo2d.swing.PDefaultScrollDirector; +import org.piccolo2d.swing.PScrollDirector; +import org.piccolo2d.swing.PScrollPane; +import org.piccolo2d.swing.PViewport; import org.piccolo2d.util.PAffineTransform; import org.piccolo2d.util.PBounds; @@ -155,9 +155,9 @@ public Point getViewPosition(final Rectangle2D viewBounds) { // First we compute the union of all the layers final PBounds layerBounds = new PBounds(); - final java.util.List layers = camera.getLayersReference(); - for (final Iterator i = layers.iterator(); i.hasNext();) { - final PLayer layer = (PLayer) i.next(); + final java.util.List layers = camera.getLayersReference(); + for (final Iterator i = layers.iterator(); i.hasNext();) { + final PLayer layer = i.next(); layerBounds.add(layer.getFullBoundsReference()); } @@ -202,9 +202,9 @@ public void setViewPosition(final double x, final double y) { // Get the union of all the layers' bounds final PBounds layerBounds = new PBounds(); - final java.util.List layers = camera.getLayersReference(); - for (final Iterator i = layers.iterator(); i.hasNext();) { - final PLayer layer = (PLayer) i.next(); + final java.util.List layers = camera.getLayersReference(); + for (final Iterator i = layers.iterator(); i.hasNext();) { + final PLayer layer = i.next(); layerBounds.add(layer.getFullBoundsReference()); } diff --git a/examples/src/main/java/org/piccolo2d/examples/SelectionExample.java b/examples/src/main/java/org/piccolo2d/examples/SelectionExample.java index 731a530c..9c306f89 100644 --- a/examples/src/main/java/org/piccolo2d/examples/SelectionExample.java +++ b/examples/src/main/java/org/piccolo2d/examples/SelectionExample.java @@ -31,11 +31,11 @@ import java.awt.Color; import org.piccolo2d.PCanvas; +import org.piccolo2d.PFrame; import org.piccolo2d.PNode; -import org.piccolo2d.extras.PFrame; -import org.piccolo2d.extras.event.PNotification; -import org.piccolo2d.extras.event.PNotificationCenter; -import org.piccolo2d.extras.event.PSelectionEventHandler; +import org.piccolo2d.event.PNotification; +import org.piccolo2d.event.PNotificationCenter; +import org.piccolo2d.event.PSelectionEventHandler; import org.piccolo2d.nodes.PPath; diff --git a/examples/src/main/java/org/piccolo2d/examples/ShadowExample.java b/examples/src/main/java/org/piccolo2d/examples/ShadowExample.java index 38ad3794..bdb6b5bc 100755 --- a/examples/src/main/java/org/piccolo2d/examples/ShadowExample.java +++ b/examples/src/main/java/org/piccolo2d/examples/ShadowExample.java @@ -30,15 +30,14 @@ import java.awt.Color; import java.awt.Graphics2D; -import java.awt.Paint; import java.awt.image.BufferedImage; import org.piccolo2d.PCanvas; -import org.piccolo2d.extras.PFrame; -import org.piccolo2d.extras.nodes.PShadow; +import org.piccolo2d.PFrame; import org.piccolo2d.nodes.PImage; import org.piccolo2d.nodes.PPath; +import org.piccolo2d.nodes.PShadow; import org.piccolo2d.nodes.PText; diff --git a/examples/src/main/java/org/piccolo2d/examples/SliderExample.java b/examples/src/main/java/org/piccolo2d/examples/SliderExample.java index be6805cc..7439a4f5 100644 --- a/examples/src/main/java/org/piccolo2d/examples/SliderExample.java +++ b/examples/src/main/java/org/piccolo2d/examples/SliderExample.java @@ -46,9 +46,9 @@ import javax.swing.SwingUtilities; import javax.swing.border.EmptyBorder; -import org.piccolo2d.extras.pswing.PSwing; -import org.piccolo2d.extras.pswing.PSwingCanvas; -import org.piccolo2d.extras.swing.PScrollPane; +import org.piccolo2d.pswing.PSwing; +import org.piccolo2d.pswing.PSwingCanvas; +import org.piccolo2d.swing.PScrollPane; /** diff --git a/examples/src/main/java/org/piccolo2d/examples/SquiggleExample.java b/examples/src/main/java/org/piccolo2d/examples/SquiggleExample.java index 3ac56682..c5d79784 100644 --- a/examples/src/main/java/org/piccolo2d/examples/SquiggleExample.java +++ b/examples/src/main/java/org/piccolo2d/examples/SquiggleExample.java @@ -33,12 +33,12 @@ import java.awt.geom.Point2D; import org.piccolo2d.PCanvas; +import org.piccolo2d.PFrame; import org.piccolo2d.PLayer; import org.piccolo2d.event.PBasicInputEventHandler; import org.piccolo2d.event.PDragSequenceEventHandler; import org.piccolo2d.event.PInputEvent; import org.piccolo2d.event.PInputEventFilter; -import org.piccolo2d.extras.PFrame; import org.piccolo2d.nodes.PPath; @@ -61,7 +61,7 @@ public SquiggleExample(final PCanvas aCanvas) { public void initialize() { super.initialize(); final PBasicInputEventHandler squiggleEventHandler = createSquiggleEventHandler(); - squiggleEventHandler.setEventFilter(new PInputEventFilter(InputEvent.BUTTON1_MASK)); + squiggleEventHandler.setEventFilter(new PInputEventFilter(InputEvent.BUTTON1_DOWN_MASK)); getCanvas().removeInputEventListener(getCanvas().getPanEventHandler()); getCanvas().addInputEventListener(squiggleEventHandler); layer = getCanvas().getLayer(); diff --git a/examples/src/main/java/org/piccolo2d/examples/StickyExample.java b/examples/src/main/java/org/piccolo2d/examples/StickyExample.java index fb8dd0d4..17298dbb 100644 --- a/examples/src/main/java/org/piccolo2d/examples/StickyExample.java +++ b/examples/src/main/java/org/piccolo2d/examples/StickyExample.java @@ -31,8 +31,8 @@ import java.awt.Color; import org.piccolo2d.PCanvas; -import org.piccolo2d.extras.PFrame; -import org.piccolo2d.extras.handles.PBoundsHandle; +import org.piccolo2d.PFrame; +import org.piccolo2d.handles.PBoundsHandle; import org.piccolo2d.nodes.PPath; diff --git a/examples/src/main/java/org/piccolo2d/examples/StickyHandleLayerExample.java b/examples/src/main/java/org/piccolo2d/examples/StickyHandleLayerExample.java index 9a6b2ce9..5a714f99 100644 --- a/examples/src/main/java/org/piccolo2d/examples/StickyHandleLayerExample.java +++ b/examples/src/main/java/org/piccolo2d/examples/StickyHandleLayerExample.java @@ -32,14 +32,14 @@ import java.util.Iterator; import org.piccolo2d.PCanvas; +import org.piccolo2d.PFrame; import org.piccolo2d.PNode; import org.piccolo2d.PRoot; import org.piccolo2d.activities.PActivity; -import org.piccolo2d.extras.PFrame; -import org.piccolo2d.extras.handles.PBoundsHandle; -import org.piccolo2d.extras.handles.PHandle; -import org.piccolo2d.extras.util.PBoundsLocator; +import org.piccolo2d.handles.PBoundsHandle; +import org.piccolo2d.handles.PHandle; import org.piccolo2d.nodes.PPath; +import org.piccolo2d.util.PBoundsLocator; /** @@ -81,9 +81,9 @@ protected void activityStep(final long elapsedTime) { final PRoot root = getActivityScheduler().getRoot(); if (root.getPaintInvalid() || root.getChildPaintInvalid()) { - final Iterator i = getCanvas().getCamera().getChildrenIterator(); + final Iterator i = getCanvas().getCamera().getChildrenIterator(); while (i.hasNext()) { - final PNode each = (PNode) i.next(); + final PNode each = i.next(); if (each instanceof PHandle) { final PHandle handle = (PHandle) each; handle.relocateHandle(); diff --git a/examples/src/main/java/org/piccolo2d/examples/StrokeExample.java b/examples/src/main/java/org/piccolo2d/examples/StrokeExample.java index 04902416..2c5547b6 100644 --- a/examples/src/main/java/org/piccolo2d/examples/StrokeExample.java +++ b/examples/src/main/java/org/piccolo2d/examples/StrokeExample.java @@ -32,10 +32,10 @@ import java.awt.Color; import org.piccolo2d.PCanvas; -import org.piccolo2d.extras.PFrame; -import org.piccolo2d.extras.util.PFixedWidthStroke; +import org.piccolo2d.PFrame; import org.piccolo2d.nodes.PPath; import org.piccolo2d.nodes.PText; +import org.piccolo2d.util.PFixedWidthStroke; /** diff --git a/examples/src/main/java/org/piccolo2d/examples/SwingLayoutExample.java b/examples/src/main/java/org/piccolo2d/examples/SwingLayoutExample.java index c7153425..7b7e1aee 100644 --- a/examples/src/main/java/org/piccolo2d/examples/SwingLayoutExample.java +++ b/examples/src/main/java/org/piccolo2d/examples/SwingLayoutExample.java @@ -55,19 +55,22 @@ import org.piccolo2d.PNode; import org.piccolo2d.event.PBasicInputEventHandler; import org.piccolo2d.event.PInputEvent; -import org.piccolo2d.extras.pswing.PSwing; -import org.piccolo2d.extras.pswing.PSwingCanvas; -import org.piccolo2d.extras.swing.SwingLayoutNode; -import org.piccolo2d.extras.swing.SwingLayoutNode.Anchor; import org.piccolo2d.nodes.PHtmlView; import org.piccolo2d.nodes.PPath; import org.piccolo2d.nodes.PText; +import org.piccolo2d.pswing.PSwing; +import org.piccolo2d.pswing.PSwingCanvas; +import org.piccolo2d.swing.SwingLayoutNode; +import org.piccolo2d.swing.SwingLayoutNode.Anchor; public class SwingLayoutExample { public static class MyPPath extends PPath.Float { - public MyPPath(final Shape shape, final Color color, final Stroke stroke, final Color strokeColor) { + + private static final long serialVersionUID = 1L; + + public MyPPath(final Shape shape, final Color color, final Stroke stroke, final Color strokeColor) { super(shape, stroke); setPaint(color); setStrokePaint(strokeColor); diff --git a/examples/src/main/java/org/piccolo2d/examples/TextExample.java b/examples/src/main/java/org/piccolo2d/examples/TextExample.java index 44f94c3f..10e39de8 100644 --- a/examples/src/main/java/org/piccolo2d/examples/TextExample.java +++ b/examples/src/main/java/org/piccolo2d/examples/TextExample.java @@ -29,8 +29,8 @@ package org.piccolo2d.examples; import org.piccolo2d.PCanvas; -import org.piccolo2d.extras.PFrame; -import org.piccolo2d.extras.event.PStyledTextEventHandler; +import org.piccolo2d.PFrame; +import org.piccolo2d.event.PStyledTextEventHandler; /** diff --git a/examples/src/main/java/org/piccolo2d/examples/TextOffsetBoundsExample.java b/examples/src/main/java/org/piccolo2d/examples/TextOffsetBoundsExample.java index 80efc1eb..3e3f2da5 100644 --- a/examples/src/main/java/org/piccolo2d/examples/TextOffsetBoundsExample.java +++ b/examples/src/main/java/org/piccolo2d/examples/TextOffsetBoundsExample.java @@ -35,9 +35,9 @@ import javax.swing.text.Document; import org.piccolo2d.PCanvas; -import org.piccolo2d.extras.PFrame; -import org.piccolo2d.extras.nodes.PStyledText; +import org.piccolo2d.PFrame; import org.piccolo2d.nodes.PHtmlView; +import org.piccolo2d.nodes.PStyledText; import org.piccolo2d.nodes.PText; diff --git a/examples/src/main/java/org/piccolo2d/examples/TextWrappingExample.java b/examples/src/main/java/org/piccolo2d/examples/TextWrappingExample.java index 0846b6e9..5464afbb 100755 --- a/examples/src/main/java/org/piccolo2d/examples/TextWrappingExample.java +++ b/examples/src/main/java/org/piccolo2d/examples/TextWrappingExample.java @@ -32,7 +32,6 @@ import java.awt.Color; import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; @@ -43,9 +42,7 @@ import javax.swing.JPanel; import org.piccolo2d.PCanvas; - -import org.piccolo2d.extras.PFrame; - +import org.piccolo2d.PFrame; import org.piccolo2d.nodes.PText; /** @@ -70,7 +67,10 @@ public class TextWrappingExample extends PFrame { /** Toggle text action. */ private final AbstractAction toggleText = new AbstractAction("Toggle text") { - /** {@inheritDoc} */ + + private static final long serialVersionUID = 1L; + + /** {@inheritDoc} */ public void actionPerformed(final ActionEvent event) { wide.setText(wide.getText() == "" ? TEXT : ""); narrow.setText(narrow.getText() == "" ? TEXT : ""); @@ -79,6 +79,9 @@ public void actionPerformed(final ActionEvent event) { /** Constrain height action. */ private final AbstractAction constrainHeight = new AbstractAction("Constrain Height") { + + private static final long serialVersionUID = 1L; + /** {@inheritDoc} */ public void actionPerformed(final ActionEvent event) { wide.setConstrainHeightToTextHeight(!wide.isConstrainHeightToTextHeight()); @@ -88,6 +91,9 @@ public void actionPerformed(final ActionEvent event) { /** Constrain width action. */ private final AbstractAction constrainWidth = new AbstractAction("Constrain Width") { + + private static final long serialVersionUID = 1L; + /** {@inheritDoc} */ public void actionPerformed(final ActionEvent event) { wide.setConstrainWidthToTextWidth(!wide.isConstrainWidthToTextWidth()); @@ -97,6 +103,9 @@ public void actionPerformed(final ActionEvent event) { /** Reset bounds action. */ private final AbstractAction resetBoundsAction = new AbstractAction("Reset Bounds") { + + private static final long serialVersionUID = 1L; + /** {@inheritDoc} */ public void actionPerformed(final ActionEvent event) { wide.setBounds(10.0d, 10.0d, 400.0d, 100.0d); diff --git a/examples/src/main/java/org/piccolo2d/examples/ToImageExample.java b/examples/src/main/java/org/piccolo2d/examples/ToImageExample.java index e1de0612..3cf17566 100644 --- a/examples/src/main/java/org/piccolo2d/examples/ToImageExample.java +++ b/examples/src/main/java/org/piccolo2d/examples/ToImageExample.java @@ -33,9 +33,9 @@ import java.awt.image.BufferedImage; import org.piccolo2d.PCanvas; +import org.piccolo2d.PFrame; import org.piccolo2d.PLayer; import org.piccolo2d.PNode; -import org.piccolo2d.extras.PFrame; import org.piccolo2d.nodes.PImage; import org.piccolo2d.nodes.PText; diff --git a/examples/src/main/java/org/piccolo2d/examples/TooltipExample.java b/examples/src/main/java/org/piccolo2d/examples/TooltipExample.java index 6d3fa07e..80292386 100644 --- a/examples/src/main/java/org/piccolo2d/examples/TooltipExample.java +++ b/examples/src/main/java/org/piccolo2d/examples/TooltipExample.java @@ -32,10 +32,10 @@ import org.piccolo2d.PCamera; import org.piccolo2d.PCanvas; +import org.piccolo2d.PFrame; import org.piccolo2d.PNode; import org.piccolo2d.event.PBasicInputEventHandler; import org.piccolo2d.event.PInputEvent; -import org.piccolo2d.extras.PFrame; import org.piccolo2d.nodes.PPath; import org.piccolo2d.nodes.PText; diff --git a/examples/src/main/java/org/piccolo2d/examples/TwoCanvasExample.java b/examples/src/main/java/org/piccolo2d/examples/TwoCanvasExample.java index 08ad315b..218cd657 100644 --- a/examples/src/main/java/org/piccolo2d/examples/TwoCanvasExample.java +++ b/examples/src/main/java/org/piccolo2d/examples/TwoCanvasExample.java @@ -32,11 +32,11 @@ import org.piccolo2d.PCamera; import org.piccolo2d.PCanvas; +import org.piccolo2d.PFrame; import org.piccolo2d.PLayer; import org.piccolo2d.PNode; import org.piccolo2d.PRoot; -import org.piccolo2d.extras.PFrame; -import org.piccolo2d.extras.handles.PBoundsHandle; +import org.piccolo2d.handles.PBoundsHandle; import org.piccolo2d.nodes.PPath; diff --git a/examples/src/main/java/org/piccolo2d/examples/WaitForActivitiesExample.java b/examples/src/main/java/org/piccolo2d/examples/WaitForActivitiesExample.java index 65c511dc..d71e3644 100644 --- a/examples/src/main/java/org/piccolo2d/examples/WaitForActivitiesExample.java +++ b/examples/src/main/java/org/piccolo2d/examples/WaitForActivitiesExample.java @@ -29,10 +29,10 @@ package org.piccolo2d.examples; import org.piccolo2d.PCanvas; +import org.piccolo2d.PFrame; import org.piccolo2d.PLayer; import org.piccolo2d.PNode; import org.piccolo2d.activities.PActivity; -import org.piccolo2d.extras.PFrame; import org.piccolo2d.nodes.PPath; diff --git a/examples/src/main/java/org/piccolo2d/examples/fisheye/CalendarNode.java b/examples/src/main/java/org/piccolo2d/examples/fisheye/CalendarNode.java index eba879ed..026d80eb 100644 --- a/examples/src/main/java/org/piccolo2d/examples/fisheye/CalendarNode.java +++ b/examples/src/main/java/org/piccolo2d/examples/fisheye/CalendarNode.java @@ -36,7 +36,10 @@ class CalendarNode extends PNode { - static int DEFAULT_NUM_DAYS = 7; + + private static final long serialVersionUID = 1L; + + static int DEFAULT_NUM_DAYS = 7; static int DEFAULT_NUM_WEEKS = 12; static int TEXT_X_OFFSET = 1; static int TEXT_Y_OFFSET = 10; diff --git a/examples/src/main/java/org/piccolo2d/examples/fisheye/DayNode.java b/examples/src/main/java/org/piccolo2d/examples/fisheye/DayNode.java index eb87e8d1..c7c4fbb5 100644 --- a/examples/src/main/java/org/piccolo2d/examples/fisheye/DayNode.java +++ b/examples/src/main/java/org/piccolo2d/examples/fisheye/DayNode.java @@ -37,15 +37,18 @@ class DayNode extends PNode { - boolean hasWidthFocus; + + private static final long serialVersionUID = 1L; + + boolean hasWidthFocus; boolean hasHeightFocus; - ArrayList lines; + ArrayList lines; int week; int day; String dayOfMonthString; public DayNode(int week, int day) { - lines = new ArrayList(); + lines = new ArrayList(); lines.add("7:00 AM Walk the dog."); lines.add("9:30 AM Meet John for Breakfast."); lines.add("12:00 PM Lunch with Peter."); diff --git a/examples/src/main/java/org/piccolo2d/examples/fisheye/TabularFisheye.java b/examples/src/main/java/org/piccolo2d/examples/fisheye/TabularFisheye.java index 39f9b08f..dc5ca543 100644 --- a/examples/src/main/java/org/piccolo2d/examples/fisheye/TabularFisheye.java +++ b/examples/src/main/java/org/piccolo2d/examples/fisheye/TabularFisheye.java @@ -36,7 +36,10 @@ public class TabularFisheye extends PCanvas { - private CalendarNode calendarNode; + + private static final long serialVersionUID = 1L; + + private CalendarNode calendarNode; public TabularFisheye() { calendarNode = new CalendarNode(); diff --git a/examples/src/main/java/org/piccolo2d/examples/fisheye/TabularFisheyeApplet.java b/examples/src/main/java/org/piccolo2d/examples/fisheye/TabularFisheyeApplet.java index 3bbb1b49..63ca23a7 100644 --- a/examples/src/main/java/org/piccolo2d/examples/fisheye/TabularFisheyeApplet.java +++ b/examples/src/main/java/org/piccolo2d/examples/fisheye/TabularFisheyeApplet.java @@ -30,6 +30,7 @@ import javax.swing.JApplet; +@SuppressWarnings({ "removal", "serial" }) public class TabularFisheyeApplet extends JApplet { public void init() { diff --git a/examples/src/main/java/org/piccolo2d/examples/fisheye/TabularFisheyeFrame.java b/examples/src/main/java/org/piccolo2d/examples/fisheye/TabularFisheyeFrame.java index 4bd0ad58..6a96a332 100644 --- a/examples/src/main/java/org/piccolo2d/examples/fisheye/TabularFisheyeFrame.java +++ b/examples/src/main/java/org/piccolo2d/examples/fisheye/TabularFisheyeFrame.java @@ -31,7 +31,10 @@ import javax.swing.JFrame; public class TabularFisheyeFrame extends JFrame { - public TabularFisheyeFrame() { + + private static final long serialVersionUID = 1L; + + public TabularFisheyeFrame() { setTitle("Piccolo2D Tabular Fisheye"); TabularFisheye tabularFisheye = new TabularFisheye(); diff --git a/examples/src/main/java/org/piccolo2d/examples/issues/ActivityMemoryLeakBugExample.java b/examples/src/main/java/org/piccolo2d/examples/issues/ActivityMemoryLeakBugExample.java index 3261558b..0f717a05 100644 --- a/examples/src/main/java/org/piccolo2d/examples/issues/ActivityMemoryLeakBugExample.java +++ b/examples/src/main/java/org/piccolo2d/examples/issues/ActivityMemoryLeakBugExample.java @@ -28,10 +28,10 @@ */ package org.piccolo2d.examples.issues; +import org.piccolo2d.PFrame; import org.piccolo2d.PLayer; import org.piccolo2d.PNode; import org.piccolo2d.activities.PActivity; -import org.piccolo2d.extras.PFrame; import org.piccolo2d.nodes.PPath; import javax.swing.SwingUtilities; @@ -49,7 +49,9 @@ */ public class ActivityMemoryLeakBugExample extends PFrame { - public static void main(String[] args) { + private static final long serialVersionUID = 1L; + + public static void main(String[] args) { new ActivityMemoryLeakBugExample(); } @@ -60,7 +62,7 @@ public void initialize() { PNode node = PPath.createEllipse(20, 20, 20, 20); layer.addChild(node); // Create a WeakReference to the node so we can detect if it is gc'd. - final WeakReference ref = new WeakReference(layer.getChild(0)); + final WeakReference ref = new WeakReference(layer.getChild(0)); // Create and execute an activity. ((PNode) ref.get()).animateToPositionScaleRotation(0, 0, 5.0, 0, 1000); // Create a Timer that will start after the activity and repeat. diff --git a/examples/src/main/java/org/piccolo2d/examples/issues/FrameCanvasSizeBugExample.java b/examples/src/main/java/org/piccolo2d/examples/issues/FrameCanvasSizeBugExample.java index 7dbfb055..5b90c733 100644 --- a/examples/src/main/java/org/piccolo2d/examples/issues/FrameCanvasSizeBugExample.java +++ b/examples/src/main/java/org/piccolo2d/examples/issues/FrameCanvasSizeBugExample.java @@ -31,7 +31,7 @@ import java.awt.Color; import org.piccolo2d.PCanvas; -import org.piccolo2d.extras.PFrame; +import org.piccolo2d.PFrame; import org.piccolo2d.nodes.PText; diff --git a/examples/src/main/java/org/piccolo2d/examples/issues/ZeroWidthStrokeBug.java b/examples/src/main/java/org/piccolo2d/examples/issues/ZeroWidthStrokeBug.java index 8006e9bb..d7d36627 100644 --- a/examples/src/main/java/org/piccolo2d/examples/issues/ZeroWidthStrokeBug.java +++ b/examples/src/main/java/org/piccolo2d/examples/issues/ZeroWidthStrokeBug.java @@ -31,8 +31,8 @@ import java.awt.BasicStroke; import org.piccolo2d.PCanvas; +import org.piccolo2d.PFrame; import org.piccolo2d.nodes.PPath; -import org.piccolo2d.extras.PFrame; /** * Example that demonstrates issue with zero width strokes in Issue 221. @@ -44,7 +44,9 @@ */ public class ZeroWidthStrokeBug extends PFrame { - public ZeroWidthStrokeBug() { + private static final long serialVersionUID = 1L; + + public ZeroWidthStrokeBug() { this(null); } diff --git a/examples/src/main/java/org/piccolo2d/examples/pswing/MultiplePSwingCanvasesExample.java b/examples/src/main/java/org/piccolo2d/examples/pswing/MultiplePSwingCanvasesExample.java index e3561f36..ad5b07de 100644 --- a/examples/src/main/java/org/piccolo2d/examples/pswing/MultiplePSwingCanvasesExample.java +++ b/examples/src/main/java/org/piccolo2d/examples/pswing/MultiplePSwingCanvasesExample.java @@ -40,14 +40,15 @@ import javax.swing.JLabel; import javax.swing.JPanel; -import org.piccolo2d.extras.pswing.PSwing; -import org.piccolo2d.extras.pswing.PSwingCanvas; +import org.piccolo2d.pswing.PSwing; +import org.piccolo2d.pswing.PSwingCanvas; public class MultiplePSwingCanvasesExample extends JFrame { - - public static void main(final String[] args) { + private static final long serialVersionUID = 1L; + + public static void main(final String[] args) { JFrame frame = new MultiplePSwingCanvasesExample(); Container container = frame.getContentPane(); @@ -96,7 +97,9 @@ private static PSwing buildTestButton() { button.addActionListener(new AbstractAction() { - public void actionPerformed(ActionEvent arg0) { + private static final long serialVersionUID = 1L; + + public void actionPerformed(ActionEvent arg0) { button.setText("Thanks"); } diff --git a/examples/src/main/java/org/piccolo2d/examples/pswing/PSwingExample1.java b/examples/src/main/java/org/piccolo2d/examples/pswing/PSwingExample1.java index 35575cb8..0cbf9df2 100644 --- a/examples/src/main/java/org/piccolo2d/examples/pswing/PSwingExample1.java +++ b/examples/src/main/java/org/piccolo2d/examples/pswing/PSwingExample1.java @@ -30,10 +30,10 @@ import org.piccolo2d.PNode; import org.piccolo2d.event.PZoomEventHandler; -import org.piccolo2d.extras.pswing.PComboBox; -import org.piccolo2d.extras.pswing.PSwing; -import org.piccolo2d.extras.pswing.PSwingCanvas; import org.piccolo2d.nodes.PText; +import org.piccolo2d.pswing.PComboBox; +import org.piccolo2d.pswing.PSwing; +import org.piccolo2d.pswing.PSwingCanvas; import javax.swing.BorderFactory; import javax.swing.JButton; diff --git a/examples/src/main/java/org/piccolo2d/examples/pswing/PSwingExample2.java b/examples/src/main/java/org/piccolo2d/examples/pswing/PSwingExample2.java index 81a34b0d..e4bfdb43 100644 --- a/examples/src/main/java/org/piccolo2d/examples/pswing/PSwingExample2.java +++ b/examples/src/main/java/org/piccolo2d/examples/pswing/PSwingExample2.java @@ -72,8 +72,8 @@ import javax.swing.table.TableColumn; import org.piccolo2d.PNode; -import org.piccolo2d.extras.pswing.PSwing; -import org.piccolo2d.extras.pswing.PSwingCanvas; +import org.piccolo2d.pswing.PSwing; +import org.piccolo2d.pswing.PSwingCanvas; /** @@ -218,13 +218,13 @@ public PSwingExample2() { panel.setBorder(new TitledBorder(new EtchedBorder(EtchedBorder.RAISED), "A JList", TitledBorder.LEFT, TitledBorder.TOP)); panel.setPreferredSize(new Dimension(200, 200)); - final Vector data = new Vector(); + final Vector data = new Vector(); data.addElement("Choice 1"); data.addElement("Choice 2"); data.addElement("Choice 3"); data.addElement("Choice 4"); data.addElement("Choice 5"); - final JList list = new JList(data); + final JList list = new JList(data); list.setBackground(Color.lightGray); panel.add(list); swing = new PSwing(panel); @@ -298,52 +298,52 @@ public void paintIcon(final Component c, final Graphics g, final int x, final in canvas.getLayer().addChild(transform); // JTable Example - final Vector columns = new Vector(); + final Vector columns = new Vector(); columns.addElement("Check Number"); columns.addElement("Description"); columns.addElement("Amount"); - final Vector rows = new Vector(); - Vector row = new Vector(); + final Vector> rows = new Vector>(); + Vector row = new Vector(); row.addElement("101"); row.addElement("Sandwich"); row.addElement("$20.00"); rows.addElement(row); - row = new Vector(); + row = new Vector(); row.addElement("102"); row.addElement("Monkey Wrench"); row.addElement("$100.00"); rows.addElement(row); - row = new Vector(); + row = new Vector(); row.addElement("214"); row.addElement("Ant farm"); row.addElement("$55.00"); rows.addElement(row); - row = new Vector(); + row = new Vector(); row.addElement("215"); row.addElement("Self-esteem tapes"); row.addElement("$37.99"); rows.addElement(row); - row = new Vector(); + row = new Vector(); row.addElement("216"); row.addElement("Tube Socks"); row.addElement("$7.45"); rows.addElement(row); - row = new Vector(); + row = new Vector(); row.addElement("220"); row.addElement("Ab Excerciser"); row.addElement("$56.95"); rows.addElement(row); - row = new Vector(); + row = new Vector(); row.addElement("319"); row.addElement("Y2K Supplies"); row.addElement("$4624.33"); rows.addElement(row); - row = new Vector(); + row = new Vector(); row.addElement("332"); row.addElement("Tie Rack"); row.addElement("$15.20"); rows.addElement(row); - row = new Vector(); + row = new Vector(); row.addElement("344"); row.addElement("Swing Set"); row.addElement("$146.59"); diff --git a/examples/src/main/java/org/piccolo2d/examples/pswing/PSwingExample3.java b/examples/src/main/java/org/piccolo2d/examples/pswing/PSwingExample3.java index 058d0a17..82362191 100644 --- a/examples/src/main/java/org/piccolo2d/examples/pswing/PSwingExample3.java +++ b/examples/src/main/java/org/piccolo2d/examples/pswing/PSwingExample3.java @@ -43,9 +43,9 @@ import javax.swing.SwingUtilities; import org.piccolo2d.PNode; -import org.piccolo2d.extras.pswing.PSwing; -import org.piccolo2d.extras.pswing.PSwingCanvas; import org.piccolo2d.nodes.PText; +import org.piccolo2d.pswing.PSwing; +import org.piccolo2d.pswing.PSwingCanvas; /** @@ -95,7 +95,9 @@ private ExampleList createSimpleComponentExamples() { JPanel examplePanel = new JPanel() { - protected void paintComponent(Graphics g) { + private static final long serialVersionUID = 1L; + + protected void paintComponent(Graphics g) { super.paintComponent(g); g.setColor(Color.RED); g.fillRect(0, 0, getWidth(), getHeight()); @@ -163,7 +165,10 @@ public static void main(final String[] args) { } class ExampleGrid extends PNode { - private int columns; + + private static final long serialVersionUID = 1L; + + private int columns; public ExampleGrid(int columns) { this.columns = columns; @@ -194,7 +199,10 @@ private double[] calculateColumnWidths() { } class ExampleList extends PText { - ExampleList(String name) { + + private static final long serialVersionUID = 1L; + + ExampleList(String name) { super(name); setScale(2); } @@ -220,7 +228,10 @@ public void addExample(String name, JComponent example) { } class ExampleNode extends PText { - ExampleNode(String name, PNode example) { + + private static final long serialVersionUID = 1L; + + ExampleNode(String name, PNode example) { super(name); addChild(example); diff --git a/examples/src/main/java/org/piccolo2d/examples/pswing/PSwingExample4.java b/examples/src/main/java/org/piccolo2d/examples/pswing/PSwingExample4.java index 19c362ec..ce28a3a2 100644 --- a/examples/src/main/java/org/piccolo2d/examples/pswing/PSwingExample4.java +++ b/examples/src/main/java/org/piccolo2d/examples/pswing/PSwingExample4.java @@ -28,13 +28,13 @@ */ package org.piccolo2d.examples.pswing; -import org.piccolo2d.extras.pswing.PSwing; - import javax.swing.JComponent; +import org.piccolo2d.pswing.PSwing; + /** - * Extends {@link PSwingExample1} but uses {@link org.piccolo2d.extras.pswing.PSwing#setUseBufferedPainting(boolean)} - * for {@link org.piccolo2d.extras.pswing.PSwing}s. + * Extends {@link PSwingExample1} but uses {@link org.piccolo2d.pswing.PSwing#setUseBufferedPainting(boolean)} + * for {@link org.piccolo2d.pswing.PSwing}s. */ public class PSwingExample4 extends PSwingExample1 { diff --git a/examples/src/main/java/org/piccolo2d/examples/pswing/issues/PSwingCameraBug.java b/examples/src/main/java/org/piccolo2d/examples/pswing/issues/PSwingCameraBug.java index 2e6c3e07..6ceae403 100644 --- a/examples/src/main/java/org/piccolo2d/examples/pswing/issues/PSwingCameraBug.java +++ b/examples/src/main/java/org/piccolo2d/examples/pswing/issues/PSwingCameraBug.java @@ -33,8 +33,8 @@ import javax.swing.JFrame; import javax.swing.JTextField; -import org.piccolo2d.extras.pswing.PSwing; -import org.piccolo2d.extras.pswing.PSwingCanvas; +import org.piccolo2d.pswing.PSwing; +import org.piccolo2d.pswing.PSwingCanvas; /** * Example that demonstrates the PSwing camera bug described in Issue 148. diff --git a/examples/src/main/java/org/piccolo2d/examples/pswing/issues/PSwingMemoryLeakExample.java b/examples/src/main/java/org/piccolo2d/examples/pswing/issues/PSwingMemoryLeakExample.java index 30864fed..a9daf33e 100644 --- a/examples/src/main/java/org/piccolo2d/examples/pswing/issues/PSwingMemoryLeakExample.java +++ b/examples/src/main/java/org/piccolo2d/examples/pswing/issues/PSwingMemoryLeakExample.java @@ -41,9 +41,9 @@ import javax.swing.Timer; import org.piccolo2d.PCanvas; -import org.piccolo2d.extras.pswing.PSwing; -import org.piccolo2d.extras.pswing.PSwingCanvas; import org.piccolo2d.nodes.PText; +import org.piccolo2d.pswing.PSwing; +import org.piccolo2d.pswing.PSwingCanvas; /** diff --git a/examples/src/main/java/org/piccolo2d/tutorial/InterfaceFrame.java b/examples/src/main/java/org/piccolo2d/tutorial/InterfaceFrame.java index b1b9a8f7..bceded02 100644 --- a/examples/src/main/java/org/piccolo2d/tutorial/InterfaceFrame.java +++ b/examples/src/main/java/org/piccolo2d/tutorial/InterfaceFrame.java @@ -33,12 +33,12 @@ import java.awt.geom.Ellipse2D; +import org.piccolo2d.PFrame; import org.piccolo2d.PLayer; import org.piccolo2d.PNode; import org.piccolo2d.event.PBasicInputEventHandler; import org.piccolo2d.event.PDragEventHandler; import org.piccolo2d.event.PInputEvent; -import org.piccolo2d.extras.PFrame; import org.piccolo2d.nodes.PImage; import org.piccolo2d.nodes.PPath; import org.piccolo2d.nodes.PText; @@ -48,9 +48,6 @@ public class InterfaceFrame extends PFrame { - /** - * - */ private static final long serialVersionUID = 1L; public void initialize() { diff --git a/examples/src/main/java/org/piccolo2d/tutorial/PiccoloPresentation.java b/examples/src/main/java/org/piccolo2d/tutorial/PiccoloPresentation.java index 886ca160..eca3812e 100644 --- a/examples/src/main/java/org/piccolo2d/tutorial/PiccoloPresentation.java +++ b/examples/src/main/java/org/piccolo2d/tutorial/PiccoloPresentation.java @@ -34,23 +34,21 @@ import java.io.File; import java.util.ArrayList; +import org.piccolo2d.PFrame; import org.piccolo2d.PNode; import org.piccolo2d.event.PBasicInputEventHandler; import org.piccolo2d.event.PInputEvent; -import org.piccolo2d.extras.PFrame; import org.piccolo2d.nodes.PImage; public class PiccoloPresentation extends PFrame { - /** - * - */ private static final long serialVersionUID = 1L; + protected PNode slideBar; protected PNode currentSlide; protected PBasicInputEventHandler eventHandler; - protected ArrayList slides = new ArrayList(); + protected ArrayList slides = new ArrayList(); public PiccoloPresentation() { super(); diff --git a/examples/src/main/java/org/piccolo2d/tutorial/SpecialEffects.java b/examples/src/main/java/org/piccolo2d/tutorial/SpecialEffects.java index 47b5d0bf..1270c535 100644 --- a/examples/src/main/java/org/piccolo2d/tutorial/SpecialEffects.java +++ b/examples/src/main/java/org/piccolo2d/tutorial/SpecialEffects.java @@ -30,10 +30,10 @@ import java.awt.Color; +import org.piccolo2d.PFrame; import org.piccolo2d.PLayer; import org.piccolo2d.PNode; import org.piccolo2d.activities.PActivity; -import org.piccolo2d.extras.PFrame; import org.piccolo2d.nodes.PPath; diff --git a/examples/src/main/java/org/piccolo2d/tutorial/UserInteraction.java b/examples/src/main/java/org/piccolo2d/tutorial/UserInteraction.java index af2440d3..ca2e151b 100644 --- a/examples/src/main/java/org/piccolo2d/tutorial/UserInteraction.java +++ b/examples/src/main/java/org/piccolo2d/tutorial/UserInteraction.java @@ -35,12 +35,12 @@ import java.awt.geom.Point2D; import org.piccolo2d.PCanvas; +import org.piccolo2d.PFrame; import org.piccolo2d.PNode; import org.piccolo2d.event.PBasicInputEventHandler; import org.piccolo2d.event.PDragSequenceEventHandler; import org.piccolo2d.event.PInputEvent; import org.piccolo2d.event.PInputEventFilter; -import org.piccolo2d.extras.PFrame; import org.piccolo2d.nodes.PPath; import org.piccolo2d.util.PDimension; @@ -122,7 +122,7 @@ public class SquiggleHandler extends PDragSequenceEventHandler { public SquiggleHandler(final PCanvas aCanvas) { canvas = aCanvas; - setEventFilter(new PInputEventFilter(InputEvent.BUTTON1_MASK)); + setEventFilter(new PInputEventFilter(InputEvent.BUTTON1_DOWN_MASK)); } public void startDrag(final PInputEvent e) { diff --git a/extras/src/main/java/org/piccolo2d/extras/PApplet.java b/extras/src/main/java/org/piccolo2d/extras/PApplet.java deleted file mode 100644 index a47c1526..00000000 --- a/extras/src/main/java/org/piccolo2d/extras/PApplet.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (c) 2008-2019, 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; - -import javax.swing.JApplet; -import javax.swing.SwingUtilities; - -import org.piccolo2d.PCanvas; - - -/** - * PApplet is meant to be subclassed by applications that just need a - * PCanvas embedded in a web page. - * - * @version 1.0 - * @author Jesse Grosjean - */ -public class PApplet extends JApplet { - /** Used to allow versioned binary streams for serializations. */ - private static final long serialVersionUID = 1L; - - /** Canvas being displayed by this applet. */ - private PCanvas canvas; - - /** - * Initializes the applet with a canvas and no background. - */ - public void init() { - setBackground(null); - - canvas = createCanvas(); - getContentPane().add(canvas); - validate(); - canvas.requestFocus(); - beforeInitialize(); - - // Manipulation of Piccolo's scene graph should be done from Swings - // event dispatch thread since Piccolo is not thread safe. This code - // calls initialize() from that thread once the PFrame is initialized, - // so you are safe to start working with Piccolo in the initialize() - // method. - SwingUtilities.invokeLater(new Runnable() { - public void run() { - PApplet.this.initialize(); - repaint(); - } - }); - } - - /** - * Returns the canvas this PApplet is displaying. - * - * @return canvas this applet is displaying - */ - public PCanvas getCanvas() { - return canvas; - } - - /** - * Provides an extension point for subclasses so that they can control - * what's on the canvas by default. - * - * @return a built canvas - */ - public PCanvas createCanvas() { - return new PCanvas(); - } - - /** - * This method will be called before the initialize() method and will be - * called on the thread that is constructing this object. - */ - public void beforeInitialize() { - } - - /** - * Subclasses should override this method and add their Piccolo2d - * initialization code there. This method will be called on the swing event - * dispatch thread. Note that the constructors of PFrame subclasses may not - * be complete when this method is called. If you need to initailize some - * things in your class before this method is called place that code in - * beforeInitialize(); - */ - public void initialize() { - } -} diff --git a/extras/src/main/java/org/piccolo2d/extras/PFrame.java b/extras/src/main/java/org/piccolo2d/extras/PFrame.java deleted file mode 100644 index e348112e..00000000 --- a/extras/src/main/java/org/piccolo2d/extras/PFrame.java +++ /dev/null @@ -1,333 +0,0 @@ -/* - * Copyright (c) 2008-2019, 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; - -import java.awt.Dimension; -import java.awt.DisplayMode; -import java.awt.GraphicsDevice; -import java.awt.GraphicsEnvironment; -import java.awt.Point; -import java.awt.Rectangle; -import java.awt.event.KeyAdapter; -import java.awt.event.KeyEvent; -import java.awt.event.KeyListener; -import java.util.ArrayList; -import java.util.Collection; -import java.util.EventListener; -import java.util.Iterator; - -import javax.swing.JFrame; -import javax.swing.SwingUtilities; - -import org.piccolo2d.PCanvas; - - -/** - * PFrame is meant to be subclassed by applications that just need a - * PCanvas in a JFrame. It also includes full screen mode functionality when run - * in JDK 1.4. These subclasses should override the initialize method and start - * adding their own code there. Look in the examples package to see lots of uses - * of PFrame. - * - * @version 1.0 - * @author Jesse Grosjean - */ -public class PFrame extends JFrame { - private static final Dimension DEFAULT_FRAME_DIMENSION = new Dimension(400, 400); - - private static final Point DEFAULT_FRAME_POSITION = new Point(100, 100); - - /** Used to allow versioned binary streams for serializations. */ - private static final long serialVersionUID = 1L; - - /** Canvas being displayed on this PFrame. */ - private PCanvas canvas; - - /** The graphics device onto which the PFrame is being displayed. */ - private final GraphicsDevice graphicsDevice; - - /** Listener that listens for escape key. */ - private transient EventListener escapeFullScreenModeListener; - - /** - * Creates a PFrame with no title, not full screen, and with the default - * canvas. - */ - public PFrame() { - this("", false, null); - } - - /** - * Creates a PFrame with the given title and with the default canvas. - * - * @param title title to display at the top of the frame - * @param fullScreenMode whether to display a full screen frame or not - * @param canvas to embed in the frame - */ - public PFrame(final String title, final boolean fullScreenMode, final PCanvas canvas) { - this(title, GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice(), fullScreenMode, canvas); - } - - /** - * Creates a PFrame with the given title and with the default canvas being - * displayed on the provided device. - * - * @param title title to display at the top of the frame - * @param device device onto which PFrame is to be displayed - * @param fullScreen whether to display a full screen frame or not - * @param canvas to embed in the frame, may be null. If so, it'll create a - * default PCanvas - */ - public PFrame(final String title, final GraphicsDevice device, final boolean fullScreen, final PCanvas canvas) { - super(title, device.getDefaultConfiguration()); - - graphicsDevice = device; - - setBackground(null); - setBounds(getDefaultFrameBounds()); - - try { - setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - } - catch (final SecurityException e) { - // expected from Applets - System.out.println("Ignoring security exception. Assuming Applet Context."); - } - - if (canvas == null) { - this.canvas = new PCanvas(); - } - else { - this.canvas = canvas; - } - - setContentPane(this.canvas); - validate(); - setFullScreenMode(fullScreen); - this.canvas.requestFocus(); - beforeInitialize(); - - // Manipulation of Piccolo's scene graph should be done from Swings - // event dispatch thread since Piccolo2D is not thread safe. This code - // calls initialize() from that thread once the PFrame is initialized, - // so you are safe to start working with Piccolo2D in the initialize() - // method. - SwingUtilities.invokeLater(new Runnable() { - public void run() { - PFrame.this.initialize(); - repaint(); - } - }); - } - - /** - * Returns the canvas being displayed on this frame. - * - * @return canvas being displayed on this frame - */ - public PCanvas getCanvas() { - return canvas; - } - - /** - * Returns the default frame bounds. - * - * @return default frame bounds - */ - public Rectangle getDefaultFrameBounds() { - return new Rectangle(DEFAULT_FRAME_POSITION, DEFAULT_FRAME_DIMENSION); - } - - /** - * Returns whether the frame is currently in full screen mode. - * - * @return whether the frame is currently in full screen mode - */ - public boolean isFullScreenMode() { - return graphicsDevice.getFullScreenWindow() != null; - } - - /** - * Switches full screen state. - * - * @param fullScreenMode whether to place the frame in full screen mode or - * not. - */ - public void setFullScreenMode(final boolean fullScreenMode) { - if (fullScreenMode != isFullScreenMode() || !isVisible()) { - if (fullScreenMode) { - switchToFullScreenMode(); - } - else { - switchToWindowedMode(); - } - } - } - - private void switchToFullScreenMode() { - addEscapeFullScreenModeListener(); - - if (isDisplayable()) { - dispose(); - } - - setUndecorated(true); - setResizable(false); - graphicsDevice.setFullScreenWindow(this); - - if (graphicsDevice.isDisplayChangeSupported()) { - chooseBestDisplayMode(graphicsDevice); - } - validate(); - } - - private void switchToWindowedMode() { - removeEscapeFullScreenModeListener(); - - if (isDisplayable()) { - dispose(); - } - - setUndecorated(false); - setResizable(true); - graphicsDevice.setFullScreenWindow(null); - validate(); - setVisible(true); - } - - /** - * Sets the display mode to the best device mode that can be determined. - * - * Used in full screen mode. - * - * @param device The graphics device being controlled. - */ - protected void chooseBestDisplayMode(final GraphicsDevice device) { - final DisplayMode best = getBestDisplayMode(device); - if (best != null) { - device.setDisplayMode(best); - } - } - - /** - * Finds the best display mode the graphics device supports. Based on the - * preferred modes. - * - * @param device the device being inspected - * - * @return best display mode the given device supports - */ - protected DisplayMode getBestDisplayMode(final GraphicsDevice device) { - final Iterator itr = getPreferredDisplayModes(device).iterator(); - while (itr.hasNext()) { - final DisplayMode each = (DisplayMode) itr.next(); - final DisplayMode[] modes = device.getDisplayModes(); - for (int i = 0; i < modes.length; i++) { - if (modes[i].getWidth() == each.getWidth() && modes[i].getHeight() == each.getHeight() - && modes[i].getBitDepth() == each.getBitDepth()) { - return each; - } - } - } - - return null; - } - - /** - * By default return the current display mode. Subclasses may override this - * method to return other modes in the collection. - * - * @param device the device being inspected - * @return preferred display mode - */ - protected Collection getPreferredDisplayModes(final GraphicsDevice device) { - final ArrayList result = new ArrayList(); - - result.add(device.getDisplayMode()); - /* - * result.add(new DisplayMode(640, 480, 32, 0)); result.add(new - * DisplayMode(640, 480, 16, 0)); result.add(new DisplayMode(640, 480, - * 8, 0)); - */ - - return result; - } - - /** - * This method adds a key listener that will take this PFrame out of full - * screen mode when the escape key is pressed. This is called for you - * automatically when the frame enters full screen mode. - */ - public void addEscapeFullScreenModeListener() { - removeEscapeFullScreenModeListener(); - escapeFullScreenModeListener = new KeyAdapter() { - public void keyPressed(final KeyEvent aEvent) { - if (aEvent.getKeyCode() == KeyEvent.VK_ESCAPE) { - setFullScreenMode(false); - } - } - }; - canvas.addKeyListener((KeyListener) escapeFullScreenModeListener); - } - - /** - * This method removes the escape full screen mode key listener. It will be - * called for you automatically when full screen mode exits, but the method - * has been made public for applications that wish to use other methods for - * exiting full screen mode. - */ - public void removeEscapeFullScreenModeListener() { - if (escapeFullScreenModeListener != null) { - canvas.removeKeyListener((KeyListener) escapeFullScreenModeListener); - escapeFullScreenModeListener = null; - } - } - - // **************************************************************** - // Initialize - // **************************************************************** - - /** - * This method will be called before the initialize() method and will be - * called on the thread that is constructing this object. - */ - public void beforeInitialize() { - } - - /** - * Subclasses should override this method and add their Piccolo2D - * initialization code there. This method will be called on the swing event - * dispatch thread. Note that the constructors of PFrame subclasses may not - * be complete when this method is called. If you need to initialize some - * things in your class before this method is called place that code in - * beforeInitialize(); - */ - public void initialize() { - } -} diff --git a/extras/src/main/java/org/piccolo2d/extras/activities/package.html b/extras/src/main/java/org/piccolo2d/extras/activities/package.html deleted file mode 100644 index d1851890..00000000 --- a/extras/src/main/java/org/piccolo2d/extras/activities/package.html +++ /dev/null @@ -1,35 +0,0 @@ - - - -

This package provides additional support for Piccolo activities.

- - diff --git a/extras/src/main/java/org/piccolo2d/extras/event/package.html b/extras/src/main/java/org/piccolo2d/extras/event/package.html deleted file mode 100644 index 0e61119f..00000000 --- a/extras/src/main/java/org/piccolo2d/extras/event/package.html +++ /dev/null @@ -1,35 +0,0 @@ - - - -

This package provides additional Piccolo event handlers.

- - diff --git a/extras/src/main/java/org/piccolo2d/extras/nodes/package.html b/extras/src/main/java/org/piccolo2d/extras/nodes/package.html deleted file mode 100644 index bcec1712..00000000 --- a/extras/src/main/java/org/piccolo2d/extras/nodes/package.html +++ /dev/null @@ -1,35 +0,0 @@ - - - -

This package contains additional nodes that may be useful for Piccolo applications.

- - diff --git a/extras/src/main/java/org/piccolo2d/extras/package.html b/extras/src/main/java/org/piccolo2d/extras/package.html deleted file mode 100644 index 73ac6e71..00000000 --- a/extras/src/main/java/org/piccolo2d/extras/package.html +++ /dev/null @@ -1,35 +0,0 @@ - - - -

Piccolo extras (piccolox) provides additional features not found in the core Piccolo library.

- - diff --git a/extras/src/main/java/org/piccolo2d/extras/util/package.html b/extras/src/main/java/org/piccolo2d/extras/util/package.html deleted file mode 100644 index fe52e9fe..00000000 --- a/extras/src/main/java/org/piccolo2d/extras/util/package.html +++ /dev/null @@ -1,35 +0,0 @@ - - - -

This package defines additional utility classes that are likely to be useful for Piccolo applications.

- - diff --git a/extras/src/main/java/overview.html b/extras/src/main/java/overview.html deleted file mode 100644 index 5e0f34e4..00000000 --- a/extras/src/main/java/overview.html +++ /dev/null @@ -1 +0,0 @@ -Writeme! diff --git a/extras/src/test/java/org/piccolo2d/extras/pswing/PSwingEventHandlerTest.java b/extras/src/test/java/org/piccolo2d/extras/pswing/PSwingEventHandlerTest.java deleted file mode 100644 index d096deab..00000000 --- a/extras/src/test/java/org/piccolo2d/extras/pswing/PSwingEventHandlerTest.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.piccolo2d.extras.pswing; - -import org.piccolo2d.extras.pswing.PSwingCanvas; -import org.piccolo2d.extras.pswing.PSwingEventHandler; - -import junit.framework.TestCase; - -public class PSwingEventHandlerTest extends TestCase { - - public void testConstructorAcceptsNullTargetNode() { - PSwingCanvas canvas = new PSwingCanvas(); - PSwingEventHandler handler = new PSwingEventHandler(canvas, null); - } -}